/*++

  Copyright (c) 1997-2011 Microsoft Corporation

  Module Name:

  XMLHELPER.CPP

Abstract:

This source file contains helper APIs for reading writing XML

Environment:

user mode

Revision History:

05-05-11 : created

--*/

/*****************************************************************************
  I N C L U D E S
 *****************************************************************************/
#include "uvcview.h"
#include "h264.h"
#include "xmlhelper.h"

// usbschema.hpp is autogenerated from schema during build PASS0
#include "usbschema.hpp"

// Include code analysis suppressions
#include "codeanalysis.h"

/*****************************************************************************
  D E F I N E S
 *****************************************************************************/
#define COBJMACROS

#define PACHAR_TO_STRING(X)   ((X != NULL)? gcnew String(Marshal::PtrToStringAnsi((IntPtr) X )):nullptr)
#define PWCHAR_TO_STRING(X)   ((X != NULL)? gcnew String(Marshal::PtrToStringUni((IntPtr) X )):nullptr)

#define MAX_STRING_DESCRIPTOR_LENGTH    512
#define STRING_DESCRIPTOR_EN_LANGUAGE_ID    0x0409
#define DEVICE_DESCRIPTOR_LENGTH    18

#define SERVICE_EHCI "usbehci"
#define SERVICE_XHCI "usbxhci"
#define SERVICE_OHCI "usbohci"
#define SERVICE_UHCI "usbuhci"

#define USB_1_1      "USB 1.1"
#define USB_2_0      "USB 2.0"
#define USB_3_0      "USB 3.0"
#define USB_GENERIC  "USB GENERIC (UNKNOWN)"

/*****************************************************************************
  N A M E S P A C E S
 *****************************************************************************/

using namespace System;
using namespace System::IO;
using namespace System::Runtime::InteropServices;
using namespace System::Collections;
using namespace Microsoft::Kits::Samples::Usb;


/*****************************************************************************
  G L O B A L S
 *****************************************************************************/

namespace Microsoft
{
    namespace Kits
    {
        namespace Samples
        {
            namespace Usb
            {
                public ref class XmlGlobal sealed
                {
                    private:
                        static XmlGlobal ^ pInstance = gcnew XmlGlobal();

                        // Empty private constructor
                        XmlGlobal()
                        {
                        }

                    public:
                        //
                        // Globals for XML view
                        //
                        property UvcViewAll ^ ViewAll;
                        property bool XmlViewInitialized;

                        //
                        // Stack of parents of a given node. This is used for finding
                        // where a given object should be added
                        //

#if CODE_ANALYSIS
                        // ParentStack need not be constant since we will only have one instance of this object
                        [SuppressMessage("Microsoft.Usage", "CA2211:NonConstantFieldsShouldNotBeVisible")]
#endif
                            static Stack ^ ParentStack = gcnew Stack();

                        static XmlGlobal ^ Instance()
                        {
                            return pInstance;
                        }
                };
            };
        };
    };
};

#define gXmlView              ((XmlGlobal::Instance())->ViewAll->UvcView)
#define gXmlViewInitialized   ((XmlGlobal::Instance())->XmlViewInitialized)
#define gXmlStack             ((XmlGlobal::Instance())->ParentStack)

/*****************************************************************************
  D E C L A R A T I O N S
 *****************************************************************************/

String ^ XmlGetStringDescriptor(UCHAR index, PSTRING_DESCRIPTOR_NODE stringDesc, bool enOnly);
void XmlAddHostControllerPowerMapping( UsbHCPowerStateMappingType ^xmlPwrInfo, PUSB_POWER_INFO usbHCPowerInfo);
String ^ XmlGetDeviceClassString(UCHAR deviceClass);
void XmlAddHostControllerPowerMapping(UsbHCPowerStateMappingType ^xmlPwrInfo, PUSB_POWER_INFO usbHCPowerInfo);
void XmlAddHub30Descriptor(Hub30DescriptorType ^hub30Desc, PUSB_30_HUB_DESCRIPTOR hub30Descriptor);
void XmlAddHubDescriptor(HubDescriptorType ^hubDesc, PUSB_HUB_DESCRIPTOR hubDescriptor);
void XmlAddPortConnectorProps(PortConnectorType ^portXmlProps, PUSB_PORT_CONNECTOR_PROPERTIES portProps);
void XmlAddHubCharacteristics(HubInformationType ^hubI, WORD hubChar);
HRESULT XmlAddHubNodeInformation(HubNodeInformationType ^ni, PUSB_NODE_INFORMATION nodeInfo);
HRESULT XmlAddHubInformationEx(HubInformationExType ^ex, PUSB_HUB_INFORMATION_EX hubInfoEx);
HRESULT XmlAddHubCapabilitiesEx(HubCapabilitiesExType ^ex, USB_HUB_CAPABILITIES_EX hubCapEx);
ExternalHubType ^ AddExternalHub(Object ^parent);
NoDeviceType ^ AddDisconnectedPort(Object ^parent);
UsbDeviceType ^ AddUsbDevice(Object ^parent);
void XmlAddEndpointDescriptor(
        EndpointDescriptorType ^usbXmlEndpointDescriptor,
        PUSB_ENDPOINT_DESCRIPTOR endPointDescriptor,
        UCHAR connectionSpeed);
void XmlAddPipeInformation(
        array< UsbPipeInfoType ^> ^ usbXmlPipeInfoList,
        PUSB_PIPE_INFO pipeInfo,
        ULONG numPipes,
        UCHAR connectionSpeed);
void XmlAddUsbDeviceDescriptor(
        UsbDeviceDescriptorType ^usbXmlDeviceDescriptor,
        PUSB_DEVICE_DESCRIPTOR usbDeviceDescriptor);
void XmlAddConfigurationDescriptor(
        UsbConfigurationDescriptorType ^ confXmlDesc,
        PUSBDEVICEINFO deviceInfo,
        PUSB_CONFIGURATION_DESCRIPTOR configDesc,
        PSTRING_DESCRIPTOR_NODE stringDesc);
void XmlAddDeviceQualDescriptor(
        UsbDeviceQualifierDescriptorType ^ qualXmlDesc,
        PUSB_DEVICE_QUALIFIER_DESCRIPTOR qualDesc);
void XmlAddDeviceConfiguration(
        UsbDeviceConfigurationType ^ confXmlDesc,
        PUSBDEVICEINFO deviceInfo,
        PUSB_CONFIGURATION_DESCRIPTOR configDesc,
        PSTRING_DESCRIPTOR_NODE stringDesc,
        int numInterfaces);
void XmlAddConnectionInfoSt(
        NodeConnectionInfoExStructType ^xmlConnectionInfoSt,
        PUSB_NODE_CONNECTION_INFORMATION_EX connectionInfo,
        PDEVICE_INFO_NODE pNode);
String ^ XmlGetLangIdString(UCHAR index, PSTRING_DESCRIPTOR_NODE stringDesc);
String ^ XmlGetStringDescriptor(UCHAR index, PSTRING_DESCRIPTOR_NODE stringDesc, bool enOnly);
String ^ XmlGetDeviceClassString(UCHAR deviceClass);
bool XmlAddDeviceClassDetails(
        UsbDeviceClassDetailsType ^ deviceDetails,
        PUSB_NODE_CONNECTION_INFORMATION_EX connectionInfo,
        PUSBDEVICEINFO deviceInfo);
void XmlAddConnectionInfo(
        NodeConnectionInfoExType ^xmlConnectionInfo,
        PUSB_NODE_CONNECTION_INFORMATION_EX connectionInfo,
        PUSBDEVICEINFO deviceInfo,
        PSTRING_DESCRIPTOR_NODE stringDesc,
        PDEVICE_INFO_NODE pNode);
void XmlAddHidDescriptor(
        UsbDeviceHidDescriptorType ^ hidXmlDesc,
        PUSB_HID_DESCRIPTOR hidDesc);
void XmlAddDeviceInterfaceDescriptor(
        UsbDeviceInterfaceDescriptorType ^ ifXmlDesc,
        PUSB_INTERFACE_DESCRIPTOR ifDesc,
        PSTRING_DESCRIPTOR_NODE stringDesc);
void XmlAddOTGDescriptor(
        UsbDeviceOTGDescriptorType ^ otgXmlDesc,
        PUSB_OTG_DESCRIPTOR otgDesc);
void XmlAddIADDescriptor(
        UsbDeviceIADDescriptorType ^ iadXmlDesc,
        PUSB_IAD_DESCRIPTOR iadDesc,
        PSTRING_DESCRIPTOR_NODE stringDesc,
        int nInterfaces);
array < UsbDeviceConfigurationType ^> ^ XmlGetConfigDescriptors(
        PUSBDEVICEINFO deviceInfo,
        PUSB_CONFIGURATION_DESCRIPTOR configDescs,
        PSTRING_DESCRIPTOR_NODE stringDesc);
UsbBosDescriptorType ^ XmlGetBosDescriptor(
        PUSB_BOS_DESCRIPTOR bosDesc,
        PSTRING_DESCRIPTOR_NODE stringDesc
        );
UsbDeviceClassType ^ XmlGetDeviceClass(UCHAR deviceClass, UCHAR deviceSubClass, UCHAR deviceProtocol);
UsbDeviceUnknownDescriptorType ^ XmlGetUnknownDescriptor(
        PUSB_COMMON_DESCRIPTOR unknownDesc
        );
UsbUsb20ExtensionDescriptorType ^ XmlGetUsb20CapabilityExtensionDescriptor(
        PUSB_DEVICE_CAPABILITY_USB20_EXTENSION_DESCRIPTOR capDesc
        );
UsbSuperSpeedExtensionDescriptorType ^ XmlGetSuperSpeedCapabilityExtensionDescriptor(
        PUSB_DEVICE_CAPABILITY_SUPERSPEED_USB_DESCRIPTOR capDesc
        );
UsbDispContIdCapExtDescriptorType ^ XmlGetContainerIdCapabilityExtensionDescriptor(
        PUSB_DEVICE_CAPABILITY_CONTAINER_ID_DESCRIPTOR capDesc
        );
UsbBillboardCapabilityDescriptorType ^ XmlGetBillboardCapabilityDescriptor(
        PUSB_DEVICE_CAPABILITY_BILLBOARD_DESCRIPTOR capDesc,
        PSTRING_DESCRIPTOR_NODE stringDesc
        );

/*****************************************************************************
  D E F I N I T I O N S
 *****************************************************************************/

/*****************************************************************************
  XmlNotifyEndOfNodeList

  This function is called back by WalkTreeTopDown() function to notify us
  that there are no more children to add for the current parent

 *****************************************************************************/
VOID XmlNotifyEndOfNodeList(PVOID pContext)
{
    UNREFERENCED_PARAMETER(pContext);

    if (gXmlStack != nullptr && gXmlStack->Count > 0)
    {
        // Remove the last parent on the stack
        gXmlStack->Pop();
    }
}

/*****************************************************************************

  XmlAddHostControllerPowerMapping()

  add power info to xml structure
 *****************************************************************************/
void XmlAddHostControllerPowerMapping(UsbHCPowerStateMappingType ^xmlPwrInfo, PUSB_POWER_INFO usbHCPowerInfo)
{
    int i, powerState;
    PUSB_POWER_INFO pUPI = usbHCPowerInfo;
    UsbHCPowerStateType ^ pwrState = nullptr;

    xmlPwrInfo->PowerMap = gcnew array<UsbHCPowerStateType ^> (WdmUsbPowerSystemShutdown);

    for(i = 0, powerState = WdmUsbPowerSystemWorking; powerState < WdmUsbPowerSystemShutdown; i++, powerState++, pUPI++)
    {
        xmlPwrInfo->PowerMap[i] = gcnew UsbHCPowerStateType();
        pwrState = xmlPwrInfo->PowerMap[i];
        pwrState->SystemState = PACHAR_TO_STRING(GetPowerStateString(pUPI->SystemState));
        pwrState->HostControllerState = PACHAR_TO_STRING(GetPowerStateString(pUPI->HcDevicePowerState));
        pwrState->HubState = PACHAR_TO_STRING(GetPowerStateString(pUPI->RhDevicePowerState));
        pwrState->CanWakeUp = pUPI->CanWakeup? true:false;
        pwrState->IsPowered = pUPI->IsPowered? true:false;
    }

    xmlPwrInfo->LastSleepState = PACHAR_TO_STRING(GetPowerStateString(pUPI->LastSystemSleepState));
    return;
}

/*****************************************************************************

  XmlAddHostController()

  Add an host controller to XML view
 *****************************************************************************/

HRESULT XmlAddHostController(PSTR hcName, PUSBHOSTCONTROLLERINFO hcInfo)
{
    HRESULT hr = S_OK;

    UNREFERENCED_PARAMETER(hcName);

    HostControllerType ^ hc = nullptr;
    //
    // Check if the USB Tree array has been initialized
    // It would have been great if XSD had a way of generating a list instead of array, but it does not
    // So we have to do array.Resize everytime
    //
    if (gXmlView->UsbTree == nullptr)
    {
        // This is the first time we are being called, initialize the array with 1 element
        gXmlView->UsbTree = gcnew array<HostControllerType ^>(1);
        gXmlView->UsbTree[0] = gcnew HostControllerType();
        hc = gXmlView->UsbTree[0];
    }
    else
    {
        // Create a new array every time as Array.Resize does not seem to work in our case (CLI)
        // We do this using ArrayList.
        ArrayList ^hcList = gcnew ArrayList;
        hcList->AddRange(gXmlView->UsbTree);
        hc = gcnew HostControllerType();
        hcList->Add(hc);
        gXmlView->UsbTree = reinterpret_cast<array<HostControllerType ^>^> (hcList->ToArray(HostControllerType::typeid));
    }

    if (hc != nullptr)
    {
        ULONG debugPort = 0;

        UsbHCDeviceInfoType ^ ci = nullptr;
        UsbHCPowerStateMappingType ^ pm = nullptr;

        if (NULL != hcInfo->UsbDeviceProperties)
        {
            hc->HwId = PACHAR_TO_STRING(hcInfo->UsbDeviceProperties->HwId);
            hc->DeviceId = PACHAR_TO_STRING(hcInfo->UsbDeviceProperties->DeviceId);
            hc->ServiceName = PACHAR_TO_STRING(hcInfo->UsbDeviceProperties->Service);
            hc->DeviceName = PACHAR_TO_STRING(hcInfo->UsbDeviceProperties->DeviceDesc);
            hc->DeviceClass = PACHAR_TO_STRING(hcInfo->UsbDeviceProperties->DeviceClass);

            bool foundUsbProtocol = false;
            if (hcInfo->UsbDeviceProperties->Service != NULL)
            {
                foundUsbProtocol = true;

                if (_stricmp(hcInfo->UsbDeviceProperties->Service, SERVICE_OHCI) == 0)
                {
                    hc->UsbProtocol = gcnew String(USB_1_1);
                }
                else if(_stricmp(hcInfo->UsbDeviceProperties->Service, SERVICE_EHCI) == 0 ||
                _stricmp(hcInfo->UsbDeviceProperties->Service, SERVICE_UHCI) == 0)
                {
                    hc->UsbProtocol = gcnew String(USB_2_0);
                }
                else if (_stricmp(hcInfo->UsbDeviceProperties->Service, SERVICE_XHCI) == 0)
                {
                    hc->UsbProtocol = gcnew String(USB_3_0);
                }
                else
                {
                    foundUsbProtocol = false;
                }
            }

            if (!foundUsbProtocol)
            {
                // If protocol lookup failed based on service name, try Controller flavor
                if(NULL != hcInfo->ControllerInfo)
                {
                    USB_CONTROLLER_FLAVOR flavor = hcInfo->ControllerInfo->ControllerFlavor;

                    if(flavor == USB_HcGeneric)
                    {
                        hc->UsbProtocol = gcnew String(USB_GENERIC);
                    }
                    else if(flavor >= OHCI_Generic && flavor < UHCI_Generic)
                    {
                        hc->UsbProtocol = gcnew String(USB_1_1);
                    }
                    else if(flavor >= UHCI_Generic && flavor <= EHCI_Generic)
                    {
                        hc->UsbProtocol = gcnew String(USB_2_0);
                    }
                    else if(flavor > EHCI_Generic)
                    {
                        hc->UsbProtocol = gcnew String(USB_3_0);
                    }
                }
            }
        }

        hc->ControllerInfo = gcnew UsbHCDeviceInfoType();
        hc->PowerMapping = gcnew UsbHCPowerStateMappingType();
        ci = hc->ControllerInfo;
        pm = hc->PowerMapping;

        ci->VendorId = hcInfo->VendorID;
        ci->DeviceId = hcInfo->DeviceID;
        ci->DriverKey = PACHAR_TO_STRING(hcInfo->DriverKey);
        ci->SubSysId = hcInfo->SubSysID;
        ci->Revision = hcInfo->Revision;

        if(NULL != hcInfo->ControllerInfo)
        {
            ci->NumberOfRootPorts = hcInfo->ControllerInfo->NumberOfRootPorts;
            ci->ControllerFlavor = hcInfo->ControllerInfo->ControllerFlavor;
            ci->PortSwitchingEnabled = (hcInfo->ControllerInfo->HcFeatureFlags & USB_HC_FEATURE_FLAG_PORT_POWER_SWITCHING)? true: false;
            ci->SelectiveSuspendEnabled = (hcInfo->ControllerInfo->HcFeatureFlags & USB_HC_FEATURE_FLAG_SEL_SUSPEND)? true: false;
            ci->LegacyBios = (hcInfo->ControllerInfo->HcFeatureFlags & USB_HC_FEATURE_LEGACY_BIOS)? true: false;
            ci->ControllerFlavorString = PACHAR_TO_STRING(GetControllerFlavorString(hcInfo->ControllerInfo->ControllerFlavor));
        }

        // Add power mappings
        XmlAddHostControllerPowerMapping(pm, (PUSB_POWER_INFO) (&(hcInfo->USBPowerInfo[0])));

        // Add debug port
        debugPort = GetEhciDebugPort(hcInfo->VendorID, hcInfo->DeviceID);
        if (debugPort > 0)
        {
            ci->DebugPort = debugPort;
        }

        gXmlStack->Push(hc);

    }
    else
    {
        hr = E_FAIL;
    }

    return hr;
}

/*****************************************************************************

  XmlAddHub30Descriptor()

  Adds the hub 3.0 descriptor to the given hub object
 *****************************************************************************/
void XmlAddHub30Descriptor(Hub30DescriptorType ^hub30Desc, PUSB_30_HUB_DESCRIPTOR hub30Descriptor)
{

    if (nullptr != hub30Desc && NULL != hub30Descriptor)
    {
        hub30Desc->Length = hub30Descriptor->bLength;
        hub30Desc->DescriptorType = hub30Descriptor->bDescriptorType;
        hub30Desc->NumberOfPorts = hub30Descriptor->bNumberOfPorts;
        hub30Desc->HubCharacteristics = hub30Descriptor->wHubCharacteristics;
        hub30Desc->PowerOntoPowerGood = hub30Descriptor->bPowerOnToPowerGood;
        hub30Desc->HubControlCurrent = hub30Descriptor->bHubControlCurrent;
        hub30Desc->HubHdrDecLat = hub30Descriptor->bHubHdrDecLat;
        hub30Desc->DeviceRemovable = hub30Descriptor->DeviceRemovable;
    }
}

/*****************************************************************************

  XmlAddHubDescriptor()

  Adds the hub descriptor to the given hub object
 *****************************************************************************/
void XmlAddHubDescriptor(HubDescriptorType ^hubDesc, PUSB_HUB_DESCRIPTOR hubDescriptor)
{
    if (nullptr != hubDesc && NULL != hubDescriptor)
    {
        hubDesc->DescriptorLength = hubDescriptor->bDescriptorLength;
        hubDesc->DescriptorType = hubDescriptor->bDescriptorType;
        hubDesc->NumberOfPorts = hubDescriptor->bNumberOfPorts;
        hubDesc->PowerOntoPowerGood = hubDescriptor->bPowerOnToPowerGood;
        hubDesc->HubControlCurrent = hubDescriptor->bHubControlCurrent;
    }
}

/*****************************************************************************

  XmlAddPortConnectorProps()

  Adds the port connector properties to XML file
 *****************************************************************************/
void XmlAddPortConnectorProps(PortConnectorType ^portXmlProps, PUSB_PORT_CONNECTOR_PROPERTIES portProps)
{
    if (NULL != portProps)
    {
        portXmlProps->UsbPortProperties = gcnew UsbPortPropertiesType();

        portXmlProps->ConnectionIndex = portProps->ConnectionIndex;
        portXmlProps->ActualLength = portProps->ActualLength;
        portXmlProps->CompanionIndex = portProps->CompanionIndex;
        portXmlProps->CompanionPortNumber = portProps->CompanionPortNumber;
        portXmlProps->CompanionHubSymbolicLinkName = PWCHAR_TO_STRING(portProps->CompanionHubSymbolicLinkName);

        portXmlProps->UsbPortProperties->PortIsUserConnectable = portProps->UsbPortProperties.PortIsUserConnectable? true:false;
        portXmlProps->UsbPortProperties->PortIsDebugCapable = portProps->UsbPortProperties.PortIsDebugCapable? true:false;
    }
    return;
}

/*****************************************************************************

  XmlAddConnectionInfoV2()

  Adds the V2 connection info structure
 *****************************************************************************/
void XmlAddConnectionInfoV2(NodeConnectionInfoExV2Type ^ connectionXmlInfo, PUSB_NODE_CONNECTION_INFORMATION_EX_V2 connectionInfo)
{
    if (NULL != connectionInfo)
    {
        connectionXmlInfo->ConnectionIndex = connectionInfo->ConnectionIndex;
        connectionXmlInfo->Length = connectionInfo->Length;

        connectionXmlInfo->Usb110Supported = connectionInfo->SupportedUsbProtocols.Usb110? true:false;
        connectionXmlInfo->Usb200Supported = connectionInfo->SupportedUsbProtocols.Usb200? true:false;
        connectionXmlInfo->Usb300Supported = connectionInfo->SupportedUsbProtocols.Usb300? true:false;

        connectionXmlInfo->DeviceIsOperatingAtSuperSpeedOrHigher =
            connectionInfo->Flags.DeviceIsOperatingAtSuperSpeedOrHigher;

        connectionXmlInfo->DeviceIsSuperSpeedCapableOrHigher =
            connectionInfo->Flags.DeviceIsSuperSpeedCapableOrHigher;

        connectionXmlInfo->DeviceIsOperatingAtSuperSpeedPlusOrHigher =
            connectionInfo->Flags.DeviceIsOperatingAtSuperSpeedPlusOrHigher;

        connectionXmlInfo->DeviceIsSuperSpeedPlusCapableOrHigher =
            connectionInfo->Flags.DeviceIsSuperSpeedPlusCapableOrHigher;

    }
    return;
}

/*****************************************************************************

  XmlAddHubCharacteristics()

  Adds the hub characteristics to the given hub object
 *****************************************************************************/
void XmlAddHubCharacteristics(HubInformationType ^hubI, WORD hubChar)
{
    HubCharacteristicsType ^hubC = nullptr;

    hubI->HubCharacteristics = gcnew HubCharacteristicsType();
    hubC = hubI->HubCharacteristics;
    hubC->HubCharacteristicsValue = hubChar;
    switch(hubChar & 0x3)
    {
        case 0x0:
            hubC->PowerSwitching = gcnew String("Ganged");
            break;
        case 0x1:
            hubC->PowerSwitching = gcnew String("Individual");
            break;
        case 0x2:
        case 0x3:
            hubC->PowerSwitching = gcnew String("None");
            break;
        default:
            hubC->PowerSwitching = gcnew String("Unknown");
    }

    hubC->CompoundDevice = (hubChar & 0x4)? true:false;

    switch(hubChar & 0x18)
    {
        case 0x0:
            hubC->OverCurrentProtection = gcnew String("Global");
            break;
        case 0x8:
            hubC->OverCurrentProtection = gcnew String("Individual");
            break;
        case 0x10:
        case 0x18:
            hubC->OverCurrentProtection = gcnew String("No protection, bus power only");
            break;
        default:
            hubC->OverCurrentProtection = gcnew String("Unknown");
    }
}

/*****************************************************************************

  XmlAddHubNodeInformation()

  Adds the node information to the hub object
 *****************************************************************************/
HRESULT XmlAddHubNodeInformation(HubNodeInformationType ^ni, PUSB_NODE_INFORMATION nodeInfo)
{
    PUSB_HUB_INFORMATION hubInfo = NULL;

    if (NULL == nodeInfo)
    {
        return E_FAIL;
    }

    hubInfo = &(nodeInfo->u.HubInformation);
    ni->HubNode = static_cast<HubNodeType> (nodeInfo->NodeType);

    ni->HubInformation = gcnew HubInformationType();
    ni->HubInformation->IsRootHub = true;
    ni->HubInformation->IsBusPowered = hubInfo->HubIsBusPowered? true:false;

    // Add hub characteristics
    XmlAddHubCharacteristics(ni->HubInformation, hubInfo->HubDescriptor.wHubCharacteristics);
    // Add descriptor
    ni->HubInformation->HubDescriptor = gcnew HubDescriptorType();
    XmlAddHubDescriptor(ni->HubInformation->HubDescriptor, &(hubInfo->HubDescriptor));

    return S_OK;
}

/*****************************************************************************

  XmlAddHubInformation()

  Adds the node information to the hub object
 *****************************************************************************/
HRESULT XmlAddHubInformationEx(HubInformationExType ^ex, PUSB_HUB_INFORMATION_EX hubInfoEx)
{
    HubDescriptorType ^hubDesc = nullptr;
    Hub30DescriptorType ^hub30Desc = nullptr;

    if (NULL == hubInfoEx)
    {
        return E_FAIL;
    }

    ex->HubType = static_cast<HubTypeType> (hubInfoEx->HubType);
    ex->HighestPortNumber = hubInfoEx->HighestPortNumber;
    switch(hubInfoEx->HubType)
    {
        case UsbRootHub:
        case Usb20Hub:
            ex->HubDescriptor = hubDesc = gcnew HubDescriptorType();
            XmlAddHubDescriptor(hubDesc, &(hubInfoEx->u.UsbHubDescriptor));
            break;
        case Usb30Hub:
            ex->Hub30Descriptor = hub30Desc = gcnew Hub30DescriptorType();
            XmlAddHub30Descriptor(hub30Desc, &(hubInfoEx->u.Usb30HubDescriptor));
            break;
    }
    return S_OK;
}

/*****************************************************************************

  XmlAddHubCapabilitiesEx()

  Adds the hub capabilities information to the hub object
 *****************************************************************************/
HRESULT XmlAddHubCapabilitiesEx(HubCapabilitiesExType ^ex, PUSB_HUB_CAPABILITIES_EX hubCapEx)
{
    if(NULL != hubCapEx)
    {
        ex->HubIsHighSpeedCapable = hubCapEx->CapabilityFlags.HubIsHighSpeedCapable?true:false;
        ex->HubIsHighSpeed  = hubCapEx->CapabilityFlags.HubIsHighSpeed?true:false;
        ex->HubIsMultiTtCapable = hubCapEx->CapabilityFlags.HubIsMultiTtCapable?true:false;
        ex->HubIsMultiTt = hubCapEx->CapabilityFlags.HubIsMultiTt?true:false;
        ex->HubIsRoot = hubCapEx->CapabilityFlags.HubIsRoot?true:false;
        ex->HubIsArmedWakeOnConnect = hubCapEx->CapabilityFlags.HubIsArmedWakeOnConnect?true:false;
        ex->HubIsBusPowered  = hubCapEx->CapabilityFlags.HubIsBusPowered?true:false;
    }
    return S_OK;
}

/*****************************************************************************

  ExternalHubType ^ AddExternalHub(Object ^parent)

  This routine finds the type of the parent and adds an external hub object to the
  parent's list of external hubs. The newly created object is returned
  We are using arrays insted of better types of collections becaused the code generated
  by xsd.exe does not support other types.
 *****************************************************************************/
ExternalHubType ^ AddExternalHub(Object ^parent)
{
    RootHubType ^ rhParent = nullptr;
    ExternalHubType ^ ehParent = nullptr;
    array<ExternalHubType ^> ^ exHubArray = nullptr;
    ExternalHubType ^ exHub = nullptr;
    boolean arrayCreated = false;

    // An external hub can be connected to a Root Hub or another External Hub
    // We need to determine the type of the object.

    // Try root hub first

    rhParent = dynamic_cast<RootHubType ^> (parent);
    if (rhParent == nullptr)
    {
        // RootHub cast was not successfult, try external hub
        ehParent = dynamic_cast<ExternalHubType ^> (parent);
        if (ehParent != nullptr)
        {
            // External hub parent
            if (ehParent->ExternalHub == nullptr)
            {
                // First hub in the list of external hubs
                ehParent->ExternalHub = gcnew array<ExternalHubType ^> (1);
                arrayCreated = true;
            }
            exHubArray = ehParent->ExternalHub;
        }

    }
    else
    {
        // Parent is a root hub
        if (rhParent->ExternalHub == nullptr)
        {
            // First hub in root hub list
            rhParent->ExternalHub = gcnew array<ExternalHubType ^>(1);
            arrayCreated = true;
        }
        exHubArray = rhParent->ExternalHub;
    }

    if (exHubArray != nullptr)
    {
        if (arrayCreated)
        {
            // We created the array in this function, so we use offset 0
            exHubArray[0] = gcnew ExternalHubType();
            exHub = exHubArray[0];
        }
        else
        {
            // The array was already present, we need to do elaborate things
            // as array.resize does not work.
            ArrayList ^exList = gcnew ArrayList();
            exList->AddRange(exHubArray);
            exHub = gcnew ExternalHubType();
            exList->Add(exHub);

            if (rhParent != nullptr)
            {
                rhParent->ExternalHub = reinterpret_cast<array<ExternalHubType ^>^> (exList->ToArray(ExternalHubType::typeid));
            }
            else
            {
                ehParent->ExternalHub = reinterpret_cast<array<ExternalHubType ^>^> (exList->ToArray(ExternalHubType::typeid));
            }
        }
    }
    return exHub;
}
/*****************************************************************************

  NoDeviceType ^ AddDisconnectedPort(Object ^parent)

  This routine finds the type of the parent and adds a empty port connection object to the
  parent's list of devices. The newly created object is returned
  We are using arrays insted of better types of collections becaused the code generated
  by xsd.exe does not support other types.
 *****************************************************************************/
NoDeviceType ^ AddDisconnectedPort(Object ^parent)
{
    RootHubType ^ rhParent = nullptr;
    ExternalHubType ^ ehParent = nullptr;
    array<NoDeviceType ^> ^ devicesArray = nullptr;
    NoDeviceType ^ noD = nullptr;
    boolean arrayCreated = false;

    // An external hub can be connected to a Root Hub or another External Hub
    // We need to determine the type of the object.

    // Try RH first

    rhParent = dynamic_cast<RootHubType ^> (parent);
    if (rhParent == nullptr)
    {
        // RootHub cast was not successfult, try external hub
        ehParent = dynamic_cast<ExternalHubType ^> (parent);
        if (ehParent != nullptr)
        {
            // External hub parent
            if (ehParent->NoDevice == nullptr)
            {
                // First hub in the list of external hubs
                ehParent->NoDevice = gcnew array<NoDeviceType ^> (1);
                arrayCreated = true;
            }
            devicesArray = ehParent->NoDevice;
        }

    }
    else
    {
        // Parent is a root hub
        if (rhParent->NoDevice == nullptr)
        {
            // First hub in root hub list
            rhParent->NoDevice = gcnew array<NoDeviceType ^>(1);
            arrayCreated = true;
        }
        devicesArray = rhParent->NoDevice;
    }

    if (devicesArray != nullptr)
    {
        if (arrayCreated)
        {
            // We created the array in this function, so we use offset 0
            devicesArray[0] = gcnew NoDeviceType();
            noD = devicesArray[0];
        }
        else
        {
            // The array was already present, we need to do elaborate things
            // as array.resize does not work.
            ArrayList ^exList = gcnew ArrayList();
            exList->AddRange(devicesArray);
            noD = gcnew NoDeviceType();
            exList->Add(noD);

            if (rhParent != nullptr)
            {
                rhParent->NoDevice = reinterpret_cast<array<NoDeviceType ^>^> (exList->ToArray(NoDeviceType::typeid));
            }
            else
            {
                ehParent->NoDevice = reinterpret_cast<array<NoDeviceType ^>^> (exList->ToArray(NoDeviceType::typeid));
            }
        }
    }
    return noD;
}

/*****************************************************************************

  UsbDeviceType ^ AddUsbDevice(Object ^parent)

  This routine finds the type of the parent and adds a port connection object to the
  parent's list of port connectors. The newly created object is returned
  We are using arrays insted of better types of collections becaused the code generated
  by xsd.exe does not support other types.
 *****************************************************************************/
UsbDeviceType ^ AddUsbDevice(Object ^parent)
{
    RootHubType ^ rhParent = nullptr;
    ExternalHubType ^ ehParent = nullptr;
    array<UsbDeviceType ^> ^ devicesArray = nullptr;
    UsbDeviceType ^ usbD = nullptr;
    boolean arrayCreated = false;

    // An external hub can be connected to a Root Hub or another External Hub
    // We need to determine the type of the object.

    // Try RH first

    rhParent = dynamic_cast<RootHubType ^> (parent);
    if (rhParent == nullptr)
    {
        // RootHub cast was not successfult, try external hub
        ehParent = dynamic_cast<ExternalHubType ^> (parent);
        if (ehParent != nullptr)
        {
            // External hub parent
            if (ehParent->UsbDevice == nullptr)
            {
                // First hub in the list of external hubs
                ehParent->UsbDevice = gcnew array<UsbDeviceType ^> (1);
                arrayCreated = true;
            }
            devicesArray = ehParent->UsbDevice;
        }

    }
    else
    {
        // Parent is a root hub
        if (rhParent->UsbDevice == nullptr)
        {
            // First hub in root hub list
            rhParent->UsbDevice = gcnew array<UsbDeviceType ^>(1);
            arrayCreated = true;
        }
        devicesArray = rhParent->UsbDevice;
    }

    if (devicesArray != nullptr)
    {
        if (arrayCreated)
        {
            // We created the array in this function, so we use offset 0
            devicesArray[0] = gcnew UsbDeviceType();
            usbD = devicesArray[0];
        }
        else
        {
            // The array was already present, we need to do elaborate things
            // as array.resize does not work.
            ArrayList ^exList = gcnew ArrayList();
            exList->AddRange(devicesArray);
            usbD = gcnew UsbDeviceType();
            exList->Add(usbD);

            if (rhParent != nullptr)
            {
                rhParent->UsbDevice = reinterpret_cast<array<UsbDeviceType ^>^> (exList->ToArray(UsbDeviceType::typeid));
            }
            else
            {
                ehParent->UsbDevice = reinterpret_cast<array<UsbDeviceType ^>^> (exList->ToArray(UsbDeviceType::typeid));
            }
        }
    }
    return usbD;
}

/*****************************************************************************

  XmlAddIADDescriptor()

  This routine adds usb IAD descriptor
 *****************************************************************************/
void XmlAddIADDescriptor(
        UsbDeviceIADDescriptorType ^ iadXmlDesc,
        PUSB_IAD_DESCRIPTOR iadDesc,
        PSTRING_DESCRIPTOR_NODE stringDesc,
        int nInterfaces)
{
    if (NULL == iadDesc || NULL == stringDesc)
    {
        return;
    }

    // Update structure fields
    iadXmlDesc->BLength = iadDesc->bLength;
    iadXmlDesc->BDescriptorType = iadDesc->bDescriptorType;
    iadXmlDesc->BFirstInterface = iadDesc->bFirstInterface;
    iadXmlDesc->BInterfaceCount = iadDesc->bInterfaceCount;
    iadXmlDesc->BFunctionClass = iadDesc->bFunctionClass;
    iadXmlDesc->BFunctionSubclass = iadDesc->bFunctionSubClass;
    iadXmlDesc->BFunctionProtocol = iadDesc->bFunctionProtocol;
    iadXmlDesc->IFunction = iadDesc->iFunction;

    // Validate fields
    if (iadDesc->bInterfaceCount == 1)
    {
        iadXmlDesc->InterfaceError = gcnew String("ERROR: bInterfaceCount must be greater than 1");
    }
    if (nInterfaces < iadDesc->bFirstInterface + iadDesc->bInterfaceCount)
    {
        iadXmlDesc->InterfaceError = gcnew String("ERROR:  The total number of interfaces");
        iadXmlDesc->InterfaceError += nInterfaces;
        iadXmlDesc->InterfaceError += " must be greater than or equal to the highest linked interface number (base ";
        iadXmlDesc->InterfaceError += iadDesc->bFirstInterface;
        iadXmlDesc->InterfaceError += " + count ";
        iadXmlDesc->InterfaceError += iadDesc->bInterfaceCount;
        iadXmlDesc->InterfaceError += " = ";
        iadXmlDesc->InterfaceError += (iadDesc->bFirstInterface + iadDesc->bInterfaceCount);
        iadXmlDesc->InterfaceError += " )";
    }
    if (iadDesc->bFunctionClass == 0)
    {
        iadXmlDesc->FunctionClassError = gcnew String("ERROR: bFunctionClass contains an illegal value 0");
    }

    iadXmlDesc->FunctionDetails = XmlGetDeviceClass(
            iadDesc->bFunctionClass,
            iadDesc->bFunctionSubClass,
            iadDesc->bFunctionProtocol);

    // Protocol check
    if (iadDesc->bFunctionClass == USB_DEVICE_CLASS_VIDEO)
    {
        if (iadDesc->bFunctionProtocol != PC_PROTOCOL_UNDEFINED)
        {
            iadXmlDesc->Protocol= gcnew String("WARNING: Protocol must be set to PC_PROTOCOL_UNDEFINED");
            iadXmlDesc->Protocol+= " for this class but is set to: ";
            iadXmlDesc->Protocol+= iadDesc->bFunctionProtocol;
        }
        else
        {
            iadXmlDesc->Protocol = gcnew String("PC_PROTOCOL_UNDEFINED protocol");
        }
    }

    if (iadDesc->iFunction)
    {
        // Add String descriptor
        iadXmlDesc->StringDesc = XmlGetStringDescriptor(
                iadDesc->iFunction,
                stringDesc,
                false);
    }

    return;
}

/*****************************************************************************

  XmlAddOTGDescriptor()

  This routine adds usb OTG descriptor
 *****************************************************************************/
void XmlAddOTGDescriptor(
        UsbDeviceOTGDescriptorType ^ otgXmlDesc,
        PUSB_OTG_DESCRIPTOR otgDesc)
{
    if (NULL == otgDesc)
    {
        return;
    }

    otgXmlDesc->BLength = otgDesc->bLength;
    otgXmlDesc->BDescriptorType = otgDesc->bDescriptorType;
    otgXmlDesc->BmAttributes = otgDesc->bmAttributes;

    // Add descriptive fields
    switch (otgDesc->bmAttributes)
    {
        case 0:
            break;
        case 1:
            otgXmlDesc->AttributesString = gcnew String("SRP support");
            break;
        case 2:
            otgXmlDesc->AttributesString = gcnew String("HNP support");
            break;
        case 3:
            otgXmlDesc->AttributesString = gcnew String("SRP and HNP support");
            break;
        default:
            otgXmlDesc->AttributesString = gcnew String("ERROR:  bmAttributes bits 2-7 are reserved should be 0)");
            break;
    }
    return;
}

/*****************************************************************************

  XmlAddHidDescriptor()

  This routine adds usb HID descriptor
 *****************************************************************************/
void XmlAddHidDescriptor(
        UsbDeviceHidDescriptorType ^ hidXmlDesc,
        PUSB_HID_DESCRIPTOR hidDesc
        )
{
    int i = 0;

    if (NULL == hidDesc)
    {
        return;
    }

    hidXmlDesc->BLength = hidDesc->bLength;
    hidXmlDesc->BDescriptorType = hidDesc->bDescriptorType;
    hidXmlDesc->BcdHID = hidDesc->bcdHID;
    hidXmlDesc->BCountryCode = hidDesc->bCountryCode;
    hidXmlDesc->BNumDescriptors = hidDesc->bNumDescriptors;

    // Add optional descriptors
    if (hidDesc->bNumDescriptors > 0)
    {
        hidXmlDesc->OptionalDescriptor = gcnew array <UsbDeviceHidOptionalDescriptorsType ^>(hidDesc->bNumDescriptors);
        for(i=0; i < hidDesc->bNumDescriptors; i++)
        {
            hidXmlDesc->OptionalDescriptor[i] = gcnew UsbDeviceHidOptionalDescriptorsType();
            hidXmlDesc->OptionalDescriptor[i]->BDescriptorType = hidDesc->OptionalDescriptors[i].bDescriptorType;
            hidXmlDesc->OptionalDescriptor[i]->WDescriptorLength = hidDesc->OptionalDescriptors[i].wDescriptorLength;
        }
    }
    return;
}

/*****************************************************************************

  XmlGetUnknownDescriptor()

  This routine gets a usb unknown descriptor object form unknown descriptor
 *****************************************************************************/
UsbDeviceUnknownDescriptorType ^ XmlGetUnknownDescriptor(
        PUSB_COMMON_DESCRIPTOR unknownDesc
        )
{
    int i = 0;
    UsbDeviceUnknownDescriptorType ^ unknownXmlDesc = nullptr;

    if (NULL == unknownDesc)
    {
        return nullptr;
    }

    unknownXmlDesc = gcnew UsbDeviceUnknownDescriptorType();
    unknownXmlDesc->BLength = unknownDesc->bLength;
    unknownXmlDesc->BDescriptorType = unknownDesc->bDescriptorType;

    // Add optional descriptors
    if (unknownDesc->bLength > 0)
    {
        unknownXmlDesc->UnknownDescriptor = gcnew String("Unknown descriptor->");
        for(i=0; i < unknownDesc->bLength; i++)
        {
            unknownXmlDesc->UnknownDescriptor += String::Format("0x{0:X} ", ((PUCHAR) unknownDesc)[i]);
        }
    }
    return unknownXmlDesc;
}

/*****************************************************************************

  XmlAddEndpointDescriptor()

  This routine adds usb endpoint descriptor and verbose fields
 *****************************************************************************/
void XmlAddEndpointDescriptor(
        EndpointDescriptorType ^usbXmlEndpointDescriptor,
        PUSB_ENDPOINT_DESCRIPTOR endPointDescriptor,
        UCHAR connectionSpeed
        )
{
    EndpointDescriptorType ^ue = usbXmlEndpointDescriptor;
    ULONG maxBytes = endPointDescriptor->wMaxPacketSize & 0x7FF;

    // Add structure values
    ue->Length = endPointDescriptor->bLength;
    ue->DescriptorType = endPointDescriptor->bDescriptorType;
    ue->EndpointAddress = endPointDescriptor->bEndpointAddress;
    ue->Attributes = endPointDescriptor->bmAttributes;
    ue->MaxPacketSize = endPointDescriptor->wMaxPacketSize;

    // Add verbose fields
    ue->EndpointId = endPointDescriptor->bEndpointAddress & 0x0F;

    // Add endpoint direction
    if (USB_ENDPOINT_DIRECTION_OUT(endPointDescriptor->bEndpointAddress))
    {
        ue->EndpointDirection = gcnew String("Out");
    }
    else if (USB_ENDPOINT_DIRECTION_IN(endPointDescriptor->bEndpointAddress))
    {
        ue->EndpointDirection = gcnew String("In");
    }

    // Add endpoint type
    switch (endPointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK)
    {
        case USB_ENDPOINT_TYPE_CONTROL:
            ue->EndpointType = gcnew String("Control Transfer Type");
            break;
        case USB_ENDPOINT_TYPE_ISOCHRONOUS:
            switch (endPointDescriptor->bmAttributes & 0x0C)
            {
                case 0x00:
                    ue->EndpointType = gcnew String("Ischronous Transfer Type - No Synchronization");
                    break;

                case 0x04:
                    ue->EndpointType = gcnew String("Ischronous Transfer Type - Asynchronous");
                    break;

                case 0x08:
                    ue->EndpointType = gcnew String("Ischronous Transfer Type - Adaptive");
                    break;

                case 0x0C:
                    ue->EndpointType = gcnew String("Ischronous Transfer Type - Synchronous");
                    break;
            }
            break;
        case USB_ENDPOINT_TYPE_BULK:
            ue->EndpointType = gcnew String("Bulk Transfer Type");
            break;

        case USB_ENDPOINT_TYPE_INTERRUPT:
            ue->EndpointType = gcnew String("Interrupt Transfer Type");
            break;
    }

    // Add packet info
    switch (connectionSpeed)
    {
        case UsbHighSpeed:
            if (endPointDescriptor->bmAttributes & 1) {
                ULONG transactions = ((endPointDescriptor->wMaxPacketSize & 0x1800) >> 11) + 1;
                // Isoc or Interrupt endpoint
                ue->EndpointPacketInfo = gcnew String(
                        transactions + " transactions per microframe, " +
                        maxBytes + " max bytes");
            }
            else
            {
                // Bulk endpoint
                ue->EndpointPacketInfo = gcnew String(maxBytes + " max bytes");
            }
            break;
        case UsbFullSpeed:
            ue->EndpointPacketInfo = gcnew String(maxBytes + " max bytes");
            break;
        default:
            // Low or Invalid speed
            ue->EndpointPacketInfo = gcnew String("Invalid bus speed");
            break;
    }

    // Add validation
    if (endPointDescriptor->wMaxPacketSize & 0xE000)
    {
        ue->EndpointPacketSizeValidation = gcnew String("ERROR: wMaxPacketSize bits 15-13 should be 0");
    } else if (connectionSpeed==UsbHighSpeed)
    {
        USHORT hsMux;

        hsMux = (endPointDescriptor->wMaxPacketSize >> 11) & 0x03;

        switch (endPointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK)
            {
            case USB_ENDPOINT_TYPE_ISOCHRONOUS:
            case USB_ENDPOINT_TYPE_INTERRUPT:
                switch (hsMux) {
                case 0:
                    if ((maxBytes < 1) || (maxBytes > 1024))
                    {
                        ue->EndpointPacketSizeValidation = gcnew String("ERROR: Invalid maximum packet size, should be between 1 and 1024");
                    }
                    break;

                case 1:
                    if ((maxBytes < 513) || (maxBytes > 1024))
                    {
                        ue->EndpointPacketSizeValidation = gcnew String("ERROR: Invalid maximum packet size, should be between 513 and 1024");
                    }
                    break;

                case 2:
                    if ((maxBytes < 683) || (maxBytes > 1024))
                    {
                        ue->EndpointPacketSizeValidation = gcnew String("ERROR: Invalid maximum packet size, should be between 683 and 1024");
                    }
                    break;

                case 3:
                    ue->EndpointPacketSizeValidation = gcnew String("ERROR: Bits 12-11 set to reserved value\r\n");
                    break;
            }
        }
    }

    // Add interval
    if (endPointDescriptor->bLength == sizeof(USB_ENDPOINT_DESCRIPTOR))
    {
        ue->Interval = endPointDescriptor->bInterval;
    }
    else
    {
        PUSB_ENDPOINT_DESCRIPTOR2 endpointDesc2 = (PUSB_ENDPOINT_DESCRIPTOR2) endPointDescriptor;
        ue->WInterval = endpointDesc2->wInterval;
        ue->SyncAddress = endpointDesc2->bSyncAddress;
    }
    return;
}

/*****************************************************************************

  XmlAddPipeInformation()

  This routine adds all the pipe information for device
 *****************************************************************************/
void XmlAddPipeInformation(
        array< UsbPipeInfoType ^> ^ usbXmlPipeInfoList,
        PUSB_PIPE_INFO pipeInfo,
        ULONG numPipes,
        UCHAR connectionSpeed
        )
{
    ULONG i = 0;

    for(i = 0; i< numPipes; i++)
    {
        // Add all pipe in the list
        usbXmlPipeInfoList[i] = gcnew UsbPipeInfoType();
        usbXmlPipeInfoList[i]->EndpointDescriptor = gcnew EndpointDescriptorType();
        XmlAddEndpointDescriptor(
                usbXmlPipeInfoList[i]->EndpointDescriptor,
                &pipeInfo[i].EndpointDescriptor,
                connectionSpeed);
        usbXmlPipeInfoList[i]->ScheduleOffset = pipeInfo->ScheduleOffset;
    }
    return;
}

/*****************************************************************************

  XmlAddUsbDeviceDescriptor()

  This routine adds usb device descriptor
 *****************************************************************************/
void XmlAddUsbDeviceDescriptor(
        UsbDeviceDescriptorType ^usbXmlDeviceDescriptor,
        PUSB_DEVICE_DESCRIPTOR usbDeviceDescriptor)
{
    UsbDeviceDescriptorType ^ud = usbXmlDeviceDescriptor;

    // Map all fields explicitly

    ud->Length = usbDeviceDescriptor->bLength;
    ud->DescriptorType = usbDeviceDescriptor->bDescriptorType;
    ud->CdUSB= usbDeviceDescriptor->bcdUSB;
    ud->DeviceClass = usbDeviceDescriptor->bDeviceClass;
    ud->DeviceSubclass = usbDeviceDescriptor->bDeviceSubClass;
    ud->DeviceProtocol = usbDeviceDescriptor->bDeviceProtocol;
    ud->MaxPacketSize0 = usbDeviceDescriptor->bMaxPacketSize0;
    ud->IdVendor = usbDeviceDescriptor->idVendor;
    ud->IdProduct = usbDeviceDescriptor->idProduct ;
    ud->CdDevice = usbDeviceDescriptor->bcdDevice;
    ud->IManufacturer = usbDeviceDescriptor->iManufacturer;
    ud->IProduct = usbDeviceDescriptor->iProduct;
    ud->ISerialNumber = usbDeviceDescriptor->iSerialNumber;
    ud->NumConfigurations = usbDeviceDescriptor->bNumConfigurations;
    return;
}

/*****************************************************************************

  XmlAddConfigurationDescriptor()

  This routine adds the configuration descriptor
 *****************************************************************************/
void XmlAddConfigurationDescriptor(
        UsbConfigurationDescriptorType ^ confXmlDesc,
        PUSBDEVICEINFO deviceInfo,
        PUSB_CONFIGURATION_DESCRIPTOR configDesc,
        PSTRING_DESCRIPTOR_NODE stringDesc
        )

{
    UINT uCount = 0;
    BOOL isSuperSpeed = FALSE;

    if (NULL == configDesc || NULL == deviceInfo)
    {
        return;
    }

    if(deviceInfo->ConnectionInfoV2 &&
       (deviceInfo->ConnectionInfoV2->Flags.DeviceIsOperatingAtSuperSpeedOrHigher ||
        deviceInfo->ConnectionInfoV2->Flags.DeviceIsOperatingAtSuperSpeedPlusOrHigher))
    {
        isSuperSpeed = TRUE;
    }

    confXmlDesc->BLength = configDesc->bLength;
    confXmlDesc->BDescriptorType = configDesc->bDescriptorType;
    confXmlDesc->WTotalLength = configDesc->wTotalLength;
    confXmlDesc->BNumInterfaces = configDesc->bNumInterfaces;
    confXmlDesc->BConfigurationValue = configDesc->bConfigurationValue;
    confXmlDesc->IConfiguration = configDesc->iConfiguration;
    confXmlDesc->BmAttributes = configDesc->bmAttributes;
    confXmlDesc->MaxPower = configDesc->MaxPower;

    uCount = GetConfigurationSize(deviceInfo);

    if (uCount != configDesc->wTotalLength)
    {
        confXmlDesc->ConfigDescError = gcnew String("ERROR: Invalid total configuration size " +
                configDesc->wTotalLength + ", should be " +  uCount);
    }

    if (configDesc->bConfigurationValue != 1)
    {
        confXmlDesc->ConfValueError = gcnew String("CAUTION: Most host controllers will only work with one configuration per speed");
    }

    if (configDesc->iConfiguration)
    {
        confXmlDesc->ConfStringDesc = XmlGetStringDescriptor(
                configDesc->iConfiguration,
                stringDesc,
                false);
    }

    if (configDesc->bmAttributes & USB_CONFIG_BUS_POWERED)
    {
        confXmlDesc->AttributesStr = gcnew String("Bus Powered");
    }
    else if (configDesc->bmAttributes & USB_CONFIG_SELF_POWERED)
    {
        confXmlDesc->AttributesStr = gcnew String("Self Powered");
    }
    else if (configDesc->bmAttributes & USB_CONFIG_REMOTE_WAKEUP)
    {
        confXmlDesc->AttributesStr = gcnew String("Remote Wakeup");
    }
    else
    {
        confXmlDesc->AttributesStr = gcnew String("WARNING: bmAttributes is using reserved space");
    }

    confXmlDesc->MaxCurrent = gcnew String("");
    confXmlDesc->MaxCurrent += (isSuperSpeed?configDesc->MaxPower * 8:configDesc->MaxPower * 2);
    confXmlDesc->MaxCurrent += " mA";

    return;
}

/*****************************************************************************

  XmlGetDeviceClass()

  This routine returns the interface class and subclass for given interface descriptor
 *****************************************************************************/
UsbDeviceClassType ^ XmlGetDeviceClass(UCHAR bInterfaceClass, UCHAR bInterfaceSubclass, UCHAR bInterfaceProtocol)
{
    String ^ deviceClass = nullptr;
    String ^ deviceSubclass = nullptr;
    UsbDeviceClassType ^ deviceDetails = gcnew UsbDeviceClassType();

    switch (bInterfaceClass)
    {
        case USB_DEVICE_CLASS_AUDIO:
            deviceClass = gcnew String("Audio Interface");

            switch (bInterfaceSubclass)
            {
                case USB_AUDIO_SUBCLASS_AUDIOCONTROL:
                    deviceSubclass = gcnew String("Audio Control Interface");
                    break;

                case USB_AUDIO_SUBCLASS_AUDIOSTREAMING:
                    deviceSubclass = gcnew String("Audio Streaming Interface");
                    break;

                case USB_AUDIO_SUBCLASS_MIDISTREAMING:
                    deviceSubclass = gcnew String("MIDI Streaming Interface");
                    break;

                default:
                    deviceSubclass = gcnew String("CAUTION: This appears to be an invalid bInterfaceSubclass : ");
                    deviceSubclass += bInterfaceSubclass;
                    break;
            }
            break;

        case USB_DEVICE_CLASS_VIDEO:
            deviceClass = gcnew String("Video Interface");

            switch(bInterfaceSubclass)
            {
                case VIDEO_SUBCLASS_CONTROL:
                    deviceSubclass = gcnew String("Video Control");
                    break;

                case VIDEO_SUBCLASS_STREAMING:
                    deviceSubclass = gcnew String("Video Streaming");
                    break;

                default:
                    deviceSubclass = gcnew String("CAUTION: This appears to be an invalid bInterfaceSubclass : ");
                    deviceSubclass += bInterfaceSubclass;
                    break;
            }
            break;

        case USB_DEVICE_CLASS_VENDOR_SPECIFIC:
            deviceClass = gcnew String("Vendor Specific Device");
            break;

        case USB_DEVICE_CLASS_HUMAN_INTERFACE:

            deviceClass = gcnew String("HID Interface");
            break;

        case USB_DEVICE_CLASS_HUB:
            deviceClass = gcnew String("HUB Interface");
            break;

        case USB_DEVICE_CLASS_RESERVED:
            deviceClass = gcnew String("CAUTION: Reserved USB Device Interface Class");
            break;

        case USB_DEVICE_CLASS_COMMUNICATIONS:
            deviceClass = gcnew String("Communications (CDC Control) USB Device\r\n");
            break;

        case USB_DEVICE_CLASS_MONITOR:
            deviceClass = gcnew String("Monitor USB Device Interface Class*** (This may be obsolete)");
            break;

        case USB_DEVICE_CLASS_PHYSICAL_INTERFACE:
            deviceClass = gcnew String("Physical Interface USB Device");
            break;

        case USB_DEVICE_CLASS_POWER:
            if (bInterfaceSubclass == 1 && bInterfaceProtocol == 1)
            {
                deviceClass = gcnew String("Image USB Device");
            }
            else
            {
                deviceClass = gcnew String("Power USB Device (This may be obsolete)");
            }
            break;

        case USB_DEVICE_CLASS_PRINTER:
            deviceClass = gcnew String("Printer USB Device");
            break;

        case USB_DEVICE_CLASS_STORAGE:
            deviceClass = gcnew String("Mass Storage USB Device");
            break;

        case USB_CDC_DATA_INTERFACE:
            deviceClass = gcnew String("CDC Data USB Device");
            break;

        case USB_CHIP_SMART_CARD_INTERFACE:
            deviceClass = gcnew String("Chip/Smart Card USB Device");
            break;

        case USB_CONTENT_SECURITY_INTERFACE:
            deviceClass = gcnew String("Content Security USB Device");
            break;

        case USB_DIAGNOSTIC_DEVICE_INTERFACE:
            if (bInterfaceSubclass == 1 && bInterfaceProtocol == 1)
            {
                deviceClass = gcnew String("Reprogrammable USB2 Compliance Diagnostic Device USB Device");
            }
            else
            {
                deviceClass = gcnew String("CAUTION: This appears to be an invalid device class: ");
                deviceClass += bInterfaceClass;
            }
            break;

        case USB_WIRELESS_CONTROLLER_INTERFACE:
            if (bInterfaceSubclass == 1 && bInterfaceProtocol == 1)
            {
                deviceClass = gcnew String("Wireless RF Controller USB Device Interface Class with Bluetooth Programming Interface");
            }
            else
            {
                deviceClass = gcnew String("CAUTION: This appears to be an invalid device class: ");
                deviceClass += bInterfaceClass;
            }
            break;

        case USB_APPLICATION_SPECIFIC_INTERFACE:
            deviceClass = gcnew String("Application Specific USB Device");

            switch(bInterfaceSubclass)
            {
                case 1:
                    deviceSubclass = gcnew String("Device Firmware Application Specific USB Device");
                    break;
                case 2:
                    deviceSubclass = gcnew String("IrDA Bridge Application Specific USB Device");
                    break;
                case 3:
                    deviceSubclass = gcnew String("Test & Measurement Class (USBTMC) Application Specific USB Device");
                    break;
                default:
                    deviceSubclass = gcnew String("CAUTION: This appears to be an invalid bInterfaceSubclass : ");
                    deviceSubclass += bInterfaceSubclass;
            }
            break;
        case USB_DEVICE_CLASS_BILLBOARD:
            deviceClass = gcnew String("Billboard Class");
            switch (bInterfaceSubclass)
            {
            case 0:
                deviceSubclass = gcnew String("Billboard Subclass");
                break;
            default:
                deviceSubclass = gcnew String("CAUTION: This appears to be an invalid bInterfaceSubClass");
                break;
            }
            break;
        default:

            deviceClass = gcnew String("Interface Class unknown : ");
            deviceClass += bInterfaceClass;
            break;
    }

    // Return class and subclass

    deviceDetails->DeviceClass = deviceClass;
    deviceDetails->DeviceSubclass = deviceSubclass;

    return deviceDetails;
}

/*****************************************************************************

  XmlAddInterfaceDescriptor()

  This routine adds the device interface descriptor
 *****************************************************************************/
void XmlAddDeviceInterfaceDescriptor(
        UsbDeviceInterfaceDescriptorType ^ ifXmlDesc,
        PUSB_INTERFACE_DESCRIPTOR ifDesc,
        PSTRING_DESCRIPTOR_NODE stringDesc)
{
    if (NULL == ifDesc || NULL == stringDesc)
    {
        return;
    }

    // Update structure fields
    ifXmlDesc->BLength = ifDesc->bLength;
    ifXmlDesc->BDescriptorType = ifDesc->bDescriptorType;
    ifXmlDesc->BInterfaceNumber = ifDesc->bInterfaceNumber;
    ifXmlDesc->BAlternateSetting = ifDesc->bAlternateSetting;
    ifXmlDesc->BNumEndpoints = ifDesc->bNumEndpoints;
    ifXmlDesc->BInterfaceClass = ifDesc->bInterfaceClass;
    ifXmlDesc->BInterfaceSubclass = ifDesc->bInterfaceSubClass;
    ifXmlDesc->BInterfaceProtocol = ifDesc->bInterfaceProtocol;
    ifXmlDesc->IInterface = ifDesc->iInterface;

    // Update class and sub class
    ifXmlDesc->InterfaceDetails = XmlGetDeviceClass(
            ifDesc->bInterfaceClass,
            ifDesc->bInterfaceSubClass,
            ifDesc->bInterfaceProtocol);

    //This is basically the check for PC_PROTOCOL_UNDEFINED
    if ((ifDesc->bInterfaceClass == USB_DEVICE_CLASS_VIDEO) ||
            (ifDesc->bInterfaceClass == USB_DEVICE_CLASS_AUDIO))
    {
        if (ifDesc->bInterfaceProtocol != PC_PROTOCOL_UNDEFINED)
        {
            ifXmlDesc->ProtocolError = gcnew String("WARNING: Protocol must be set to PC_PROTOCOL_UNDEFINED");
            ifXmlDesc->ProtocolError += " for this class but is set to: ";
            ifXmlDesc->ProtocolError += ifDesc->bInterfaceProtocol;
        }
    }

    if (ifDesc->iInterface)
    {
        // Add String descriptor
        ifXmlDesc->StringDesc = XmlGetStringDescriptor(
                ifDesc->iInterface,
                stringDesc,
                false);
    }

    if (ifDesc->bLength == sizeof(USB_INTERFACE_DESCRIPTOR2))
    {
        PUSB_INTERFACE_DESCRIPTOR2 interfaceDesc2;

        interfaceDesc2 = (PUSB_INTERFACE_DESCRIPTOR2)ifDesc;

        ifXmlDesc->WNumClasses = interfaceDesc2->wNumClasses;
    }
    return;
}

/*****************************************************************************

  XmlAddDeviceQualDescriptor()

  This routine adds the device qualifier descriptor
 *****************************************************************************/

void XmlAddDeviceQualDescriptor(
        UsbDeviceQualifierDescriptorType ^ qualXmlDesc,
        PUSB_DEVICE_QUALIFIER_DESCRIPTOR qualDesc)
{
    if (NULL == qualDesc)
    {
        return;
    }

    // Add structure fields
    qualXmlDesc->BLength = qualDesc->bLength;
    qualXmlDesc->BDescriptorType = qualDesc->bDescriptorType;
    qualXmlDesc->BcdUSB = qualDesc->bcdUSB;
    qualXmlDesc->BDeviceClass = qualDesc->bDeviceClass;
    qualXmlDesc->BDeviceSubclass = qualDesc->bDeviceSubClass;
    qualXmlDesc->BDeviceProtocol = qualDesc->bDeviceProtocol;
    qualXmlDesc->BMaxPacketSize0 = qualDesc->bMaxPacketSize0;
    qualXmlDesc->NumConfigurations = qualDesc->bNumConfigurations;

    // Get device class string
    qualXmlDesc->DeviceClass = XmlGetDeviceClassString(qualDesc->bDeviceClass);

    if (qualDesc->bDeviceSubClass > 0x00 && qualDesc->bDeviceSubClass < 0xFF)
    {
        qualXmlDesc->DeviceSubclassError = gcnew String("ERROR:  bDeviceSubClass is invalid : ");
        qualXmlDesc->DeviceSubclassError += qualDesc->bDeviceSubClass;
    }

    if (qualDesc->bDeviceProtocol > 0x00 && qualDesc->bDeviceProtocol < 0xFF)
    {
        qualXmlDesc->DeviceProtocolError = gcnew String("ERROR:  bDeviceProtocol is invalid : ");
        qualXmlDesc->DeviceProtocolError += qualDesc->bDeviceProtocol;
    }

    qualXmlDesc->MaxPacketSizeInBytes = qualDesc->bMaxPacketSize0;

    if (qualDesc->bNumConfigurations != 1)
    {
        qualXmlDesc->DeviceNumConfigError = gcnew String(
                "CAUTION: Most host controllers will only work with one configuration per speed");
    }

    if (qualDesc->bReserved != 0)
    {
        qualXmlDesc->ReservedError = gcnew String("WARNING: bReserved needs to be set to 0 to be valid - " +
                                                        qualDesc->bReserved);
    }

    return;
}

/*****************************************************************************

  XmlAddAddConfigDescriptors()

  This routine adds the all the config descriptors
 *****************************************************************************/
array < UsbDeviceConfigurationType ^> ^ XmlGetConfigDescriptors(
        PUSBDEVICEINFO deviceInfo,
        PUSB_CONFIGURATION_DESCRIPTOR configDescs,
        PSTRING_DESCRIPTOR_NODE stringDesc
        )
{
    array < UsbDeviceConfigurationType ^> ^ confXmlDescs = nullptr;
    PUSB_COMMON_DESCRIPTOR commonDesc = NULL;
    PUCHAR descEnd = NULL;
    ArrayList ^confList = gcnew ArrayList;
    UsbDeviceConfigurationType ^ deviceConf = nullptr;

    commonDesc = (PUSB_COMMON_DESCRIPTOR) configDescs;
    descEnd = (PUCHAR) configDescs + configDescs->wTotalLength;

    while ((PUCHAR)commonDesc + sizeof(USB_COMMON_DESCRIPTOR) < descEnd &&
        (PUCHAR)commonDesc + commonDesc->bLength <= descEnd)
    {
        // Add the config descriptor
        deviceConf = gcnew UsbDeviceConfigurationType();

        XmlAddDeviceConfiguration(
                deviceConf,
                deviceInfo,
                (PUSB_CONFIGURATION_DESCRIPTOR) commonDesc,
                stringDesc,
                configDescs->bNumInterfaces
                );

        confList->Add(deviceConf);
        commonDesc = (PUSB_COMMON_DESCRIPTOR) ((PUCHAR) commonDesc + commonDesc->bLength);
    }

    confXmlDescs = reinterpret_cast<array<UsbDeviceConfigurationType ^>^> (confList->ToArray(UsbDeviceConfigurationType::typeid));
    return confXmlDescs;
}

/*****************************************************************************

  XmlAddDeviceConfiguration()

  This routine adds the device configuration
 *****************************************************************************/
void XmlAddDeviceConfiguration(
        UsbDeviceConfigurationType ^ confXmlDesc,
        PUSBDEVICEINFO deviceInfo,
        PUSB_CONFIGURATION_DESCRIPTOR configDesc,
        PSTRING_DESCRIPTOR_NODE stringDesc,
        int numInterfaces
        )
{
    PUSB_COMMON_DESCRIPTOR commonDesc = NULL;
    UCHAR bInterfaceClass = 0;
    UCHAR bInterfaceSubclass = 0;
    UCHAR bInterfaceProtocol = 0;
    BOOL displayUnknown = FALSE;

    if (NULL == deviceInfo || NULL == configDesc || NULL == stringDesc)
    {
        return;
    }

    commonDesc = (PUSB_COMMON_DESCRIPTOR)configDesc;
    displayUnknown = FALSE;

    switch (commonDesc->bDescriptorType)
    {
        case USB_DEVICE_QUALIFIER_DESCRIPTOR_TYPE:
            if (commonDesc->bLength != sizeof(USB_DEVICE_QUALIFIER_DESCRIPTOR))
            {
                // Validate descriptor
                confXmlDesc->DeviceQualifierError = String::Format(
                        "ERROR: Device Qualifier bLength value incorrect Obtained: {0} Expected {1}",
                        commonDesc->bLength,
                        sizeof(USB_DEVICE_QUALIFIER_DESCRIPTOR));
                displayUnknown = TRUE;
                break;
            }
            // Add device Qual descriptor
            confXmlDesc->DeviceQualifierDescriptor = gcnew UsbDeviceQualifierDescriptorType();

            XmlAddDeviceQualDescriptor(
                 confXmlDesc->DeviceQualifierDescriptor,
                 (PUSB_DEVICE_QUALIFIER_DESCRIPTOR) commonDesc);
            break;

        case USB_OTHER_SPEED_CONFIGURATION_DESCRIPTOR_TYPE:
            if (commonDesc->bLength != sizeof(USB_CONFIGURATION_DESCRIPTOR))
            {
                // Validate descriptor
                confXmlDesc->SpeedConfigurationError = String::Format(
                        "ERROR: Other speed configuration bLength value incorrect Obtained: {0} Expected {1}",
                        commonDesc->bLength,
                        sizeof(USB_CONFIGURATION_DESCRIPTOR));
                displayUnknown = TRUE;
            }

            // Add configuration desc
            confXmlDesc->ConfigurationDescriptor = gcnew UsbConfigurationDescriptorType();

            XmlAddConfigurationDescriptor(
                    confXmlDesc->ConfigurationDescriptor,
                    deviceInfo,
                    (PUSB_CONFIGURATION_DESCRIPTOR) commonDesc,
                    stringDesc);
            break;

        case USB_CONFIGURATION_DESCRIPTOR_TYPE:
            if (commonDesc->bLength != sizeof(USB_CONFIGURATION_DESCRIPTOR))
            {
                // Validate descriptor
                confXmlDesc->SpeedConfigurationError = String::Format(
                        "ERROR: Configuration bLength value incorrect Obtained: {0} Expected {1}",
                        commonDesc->bLength,
                        sizeof(USB_CONFIGURATION_DESCRIPTOR));
                displayUnknown = TRUE;
                break;
            }

            // Add configuration desc
            confXmlDesc->ConfigurationDescriptor = gcnew UsbConfigurationDescriptorType();
            XmlAddConfigurationDescriptor(
                    confXmlDesc->ConfigurationDescriptor,
                    deviceInfo,
                    (PUSB_CONFIGURATION_DESCRIPTOR) commonDesc,
                    stringDesc);
            break;

        case USB_INTERFACE_DESCRIPTOR_TYPE:
            if ((commonDesc->bLength != sizeof(USB_INTERFACE_DESCRIPTOR)) &&
                    (commonDesc->bLength != sizeof(USB_INTERFACE_DESCRIPTOR2)))
            {
                // Validate descriptor
                confXmlDesc->InterfaceError = String::Format(
                        "ERROR: Interface bLength value incorrect Obtained: {0} Expected: {1} or {2}",
                        commonDesc->bLength,
                        sizeof(USB_INTERFACE_DESCRIPTOR),
                        sizeof(USB_INTERFACE_DESCRIPTOR2));
                displayUnknown = TRUE;
                break;
            }

            // Add interface descriptor
            bInterfaceClass = ((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->bInterfaceClass;
            bInterfaceSubclass = ((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->bInterfaceSubClass;
            bInterfaceProtocol = ((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->bInterfaceProtocol;

            confXmlDesc->InterfaceDescriptor = gcnew UsbDeviceInterfaceDescriptorType();
            XmlAddDeviceInterfaceDescriptor(
                    confXmlDesc->InterfaceDescriptor,
                    (PUSB_INTERFACE_DESCRIPTOR) commonDesc,
                    stringDesc
                    );

        case USB_ENDPOINT_DESCRIPTOR_TYPE:
            if ((commonDesc->bLength != sizeof(USB_ENDPOINT_DESCRIPTOR)) &&
                    (commonDesc->bLength != sizeof(USB_ENDPOINT_DESCRIPTOR2)))
            {
                // Validate endpoint descriptor
                confXmlDesc->EndpointError = String::Format(
                        "ERROR: Endpoint bLength value incorrect Obtained: {0} Expected: {1} or {2}",
                        commonDesc->bLength,
                        sizeof(USB_ENDPOINT_DESCRIPTOR),
                        sizeof(USB_ENDPOINT_DESCRIPTOR2));
                displayUnknown = TRUE;
                break;
            }

            confXmlDesc->EndpointDescriptor = gcnew EndpointDescriptorType();

            if (NULL != deviceInfo->ConnectionInfo)
            {
                // Add endpoint descriptor
                XmlAddEndpointDescriptor(
                        confXmlDesc->EndpointDescriptor,
                        (PUSB_ENDPOINT_DESCRIPTOR) commonDesc,
                        deviceInfo->ConnectionInfo->Speed);
            }

            break;

        case USB_HID_DESCRIPTOR_TYPE:
            if (commonDesc->bLength < sizeof(USB_HID_DESCRIPTOR))
            {
                // Validate HID
                confXmlDesc->HidError = String::Format(
                        "ERROR: HID bLength value incorrect Obtained: {0} Expected: {1}",
                        commonDesc->bLength,
                        sizeof(USB_HID_DESCRIPTOR));
                displayUnknown = TRUE;
                break;
            }

            // Add HID descriptor
            confXmlDesc->HidDescriptor = gcnew UsbDeviceHidDescriptorType();
            XmlAddHidDescriptor(
                    confXmlDesc->HidDescriptor,
                    (PUSB_HID_DESCRIPTOR) commonDesc
                    );
            break;

        case USB_OTG_DESCRIPTOR_TYPE:
            if (commonDesc->bLength < sizeof(USB_OTG_DESCRIPTOR))
            {
                // Validate length
                confXmlDesc->HidError = String::Format(
                        "ERROR: OTG bLength value incorrect Obtained: {0} Expected: {1}",
                        commonDesc->bLength,
                        sizeof(USB_OTG_DESCRIPTOR));
                displayUnknown = TRUE;
                break;
            }

            // Add OTG descriptor
            confXmlDesc->OtgDescriptor = gcnew UsbDeviceOTGDescriptorType();
            XmlAddOTGDescriptor(
                    confXmlDesc->OtgDescriptor,
                    (PUSB_OTG_DESCRIPTOR) commonDesc
                    );
            break;

        case USB_IAD_DESCRIPTOR_TYPE:
            if (commonDesc->bLength < sizeof(USB_IAD_DESCRIPTOR))
            {
                // Validate length
                confXmlDesc->IadError = String::Format(
                        "ERROR: IAD bLength value incorrect",
                        commonDesc->bLength,
                        sizeof(USB_OTG_DESCRIPTOR));
                displayUnknown = TRUE;
            }

            // Add IAD descriptor
            confXmlDesc->IadDescriptor = gcnew UsbDeviceIADDescriptorType();
            XmlAddIADDescriptor(
                    confXmlDesc->IadDescriptor,
                    (PUSB_IAD_DESCRIPTOR) commonDesc,
                    stringDesc,
                    numInterfaces
                    );
            break;

        default:
            // Interface class device (?)
            confXmlDesc->DeviceDetails = XmlGetDeviceClass(
                    ((PUSB_INTERFACE_DESCRIPTOR) commonDesc)->bInterfaceClass,
                    ((PUSB_INTERFACE_DESCRIPTOR) commonDesc)->bInterfaceSubClass,
                    ((PUSB_INTERFACE_DESCRIPTOR) commonDesc)->bInterfaceProtocol
                    );
            break;
    }

    if (displayUnknown)
    {
        // Add unknown descriptor
        confXmlDesc->UnknownDescriptor = XmlGetUnknownDescriptor(commonDesc);
    }
    return;
}

/*****************************************************************************

  XmlAddConnectionInfoSt()

  This routine adds connection information structures for the device
 *****************************************************************************/
void XmlAddConnectionInfoSt(
        NodeConnectionInfoExStructType ^xmlConnectionInfoSt,
        PUSB_NODE_CONNECTION_INFORMATION_EX connectionInfo,
        PDEVICE_INFO_NODE pNode)
{
    NodeConnectionInfoExStructType ^nS = xmlConnectionInfoSt;

    nS->ConnectionIndex = connectionInfo->ConnectionIndex;

    nS->DeviceDescriptor = gcnew UsbDeviceDescriptorType();

    XmlAddUsbDeviceDescriptor(nS->DeviceDescriptor, &(connectionInfo->DeviceDescriptor));

    nS->CurrentConfigurationValue = connectionInfo->CurrentConfigurationValue;
    nS->Speed = connectionInfo->Speed;
    nS->SpeedStr = static_cast<UsbConnectionSpeedType> (connectionInfo->Speed);
    nS->DeviceIsHub = connectionInfo->DeviceIsHub? true: false;
    nS->NumOfOpenPipes = connectionInfo->NumberOfOpenPipes;
    nS->UsbConnectionStatus = static_cast<UsbConnectionStatusType> (connectionInfo->ConnectionStatus);

    if(NULL != pNode)
    {
        nS->DevicePowerState = static_cast<DevicePowerStateType>(pNode->LatestDevicePowerState);
    }
    else
    {
        nS->DevicePowerState = static_cast<DevicePowerStateType>(PowerDeviceUnspecified);
    }

    // Add the pipe list
    if (connectionInfo->NumberOfOpenPipes > 0)
    {
        nS->Pipe =  gcnew array <UsbPipeInfoType ^>(connectionInfo->NumberOfOpenPipes);
        XmlAddPipeInformation(
                nS->Pipe,
                connectionInfo->PipeList,
                connectionInfo->NumberOfOpenPipes,
                connectionInfo->Speed
                );
    }

    return;
}

/*****************************************************************************

  XmlGetLangIdString()

  Obtains the language string for given string descriptor index
 *****************************************************************************/
String ^ XmlGetLangIdString(UCHAR index, PSTRING_DESCRIPTOR_NODE stringDesc)
{
    String ^langIdStr = nullptr;
    bool foundDescriptor = false;

    while(stringDesc)
    {
        if (stringDesc->DescriptorIndex == index)
        {
            langIdStr = PACHAR_TO_STRING(GetLangIDString(stringDesc->LanguageID));

            if (langIdStr == nullptr)
            {
                langIdStr = gcnew String("WARNING: Invalid language ID: " + stringDesc->LanguageID);
            }
            foundDescriptor = true;
            break;
        }
        stringDesc = stringDesc->Next;
    }

    if (foundDescriptor == false)
    {
        // If no descriptor was found, return error message in field
        langIdStr = gcnew String("ERROR: No String descriptor for index " + index);
    }

    return langIdStr;
}

/*****************************************************************************

  XmlGetStringDescriptor()

  Obtains the string descriptor for given string descriptor index
 *****************************************************************************/

String ^ XmlGetStringDescriptor(UCHAR index, PSTRING_DESCRIPTOR_NODE stringDesc, bool enOnly)
{
    ULONG nBytes = 0;

    CHAR pString[MAX_STRING_DESCRIPTOR_LENGTH];
    String ^desc = nullptr;
    bool foundDescriptor = false;
    bool foundNonEnglishDescriptor = false;

    ZeroMemory(pString, MAX_STRING_DESCRIPTOR_LENGTH);

    while(stringDesc)
    {
        if (stringDesc->DescriptorIndex == index)
        {
            if (enOnly && stringDesc->LanguageID != STRING_DESCRIPTOR_EN_LANGUAGE_ID)
            {
                // If we are required to return only english descriptor, continue
                foundNonEnglishDescriptor = true;
                continue;
            }

            nBytes = WideCharToMultiByte(
                    CP_ACP,
                    WC_NO_BEST_FIT_CHARS,
                    stringDesc->StringDescriptor->bString,
                    (stringDesc->StringDescriptor->bLength -2)/2,
                    pString,
                    MAX_STRING_DESCRIPTOR_LENGTH,
                    NULL,
                    NULL
                    );

            if (nBytes)
            {
                foundDescriptor = true;
                desc = PACHAR_TO_STRING(pString);
            }
            break;
        }
        stringDesc = stringDesc->Next;
    }

    if ((foundDescriptor == false) && (foundNonEnglishDescriptor == false))
    {
        // If no descriptor was found, return error message in field
        desc = gcnew String("ERROR: No String descriptor for index " +
                    index);
    }
    else if ((foundDescriptor == false) && (foundNonEnglishDescriptor == true) && (enOnly))
    {
        desc = gcnew String("ERROR: The index " + index + " does not support English(US)");
    }

    return desc;
}

/*****************************************************************************

  XmlGetDeviceClassString()

  Returns the device class string for given device class ID
 *****************************************************************************/

String ^ XmlGetDeviceClassString(UCHAR deviceClass)
{
    String ^ deviceClassStr = nullptr;

    // Not an IAD device
    switch (deviceClass)
    {
        case USB_INTERFACE_CLASS_DEVICE:
            deviceClassStr = gcnew String("Interface Class Defined Device");
            break;

        case USB_COMMUNICATION_DEVICE:
            deviceClassStr = gcnew String("Communication Device");
            break;

        case USB_HUB_DEVICE:
            deviceClassStr = gcnew String("Hub Device");
            break;

        case USB_DIAGNOSTIC_DEVICE:
            deviceClassStr = gcnew String("Diagnostic Device");
            break;

        case USB_WIRELESS_CONTROLLER_DEVICE:
            deviceClassStr = gcnew String("Wireless Controller(Bluetooth) Device");
            break;

        case USB_VENDOR_SPECIFIC_DEVICE:
            deviceClassStr = gcnew String("Vendor specific device");
            break;

        case USB_DEVICE_CLASS_BILLBOARD:
            deviceClassStr = gcnew String("Billboard class device");
            break;

        default:
            deviceClassStr= gcnew String("ERROR: unknown bDeviceClass" + deviceClass);
            break;
    }
    return deviceClassStr;
}


/*****************************************************************************

  XmlAddDeviceClassDetails()

  This routine adds device class details
 *****************************************************************************/
bool XmlAddDeviceClassDetails(
        UsbDeviceClassDetailsType ^ deviceDetails,
        PUSB_NODE_CONNECTION_INFORMATION_EX connectionInfo,
        PUSBDEVICEINFO deviceInfo)
{
    UINT uIADcount = 0;
    bool tog = true;

    uIADcount = IsIADDevice((PUSBDEVICEINFO) deviceInfo);

    if (uIADcount)
    {
        // IAD device, check validity of device class
        if (connectionInfo->DeviceDescriptor.bDeviceClass == USB_MISCELLANEOUS_DEVICE)
        {
            tog = false;
            deviceDetails->DeviceType = gcnew String("Multi-interface Function Code Device");
        }
        else {
            deviceDetails->DeviceTypeError = gcnew String("ERROR: device class should be Multi-interface Function " +
                                                          USB_MISCELLANEOUS_DEVICE +
                                                          "is used");
        }
        deviceDetails->UvcVersion = IsUVCDevice((PUSBDEVICEINFO) deviceInfo);

        // This device configuration has 1 or more IAD descriptors
        if (connectionInfo->DeviceDescriptor.bDeviceSubClass == USB_COMMON_SUB_CLASS)
        {
            deviceDetails->SubclassType = gcnew String("Common Class Sub Class");
        }
        else
        {
            deviceDetails->SubclassTypeError = gcnew String("ERROR: device SubClass should be USB Common Sub Class" +
                                                            USB_COMMON_SUB_CLASS +
                                                            " when IAD descriptor is used");
        }

        // Check device protocol
        if (connectionInfo->DeviceDescriptor.bDeviceProtocol == USB_IAD_PROTOCOL)
        {
            deviceDetails->DeviceProtocol = gcnew String("Interface Association Descriptor protocol");
        }
        else
        {
            deviceDetails->DeviceProtocolError = gcnew String("ERROR: device Protocol should be USB IAD Protocol " +
                                                               USB_IAD_PROTOCOL +
                                                              " when IAD descriptor is used");
        }

    }
    else
    {
        deviceDetails->DeviceType =  XmlGetDeviceClassString(connectionInfo->DeviceDescriptor.bDeviceClass);

        if (connectionInfo->DeviceDescriptor.bDeviceClass == USB_DEVICE_CLASS_BILLBOARD &&
            (connectionInfo->DeviceDescriptor.bDeviceSubClass != 0x0 ||
            connectionInfo->DeviceDescriptor.bDeviceProtocol != 0x0))
        {
            deviceDetails->DeviceTypeError = gcnew String("ERROR: Billboard device has invalid bDeviceSubclass/bDeviceProtocol");
        }

        if (connectionInfo->DeviceDescriptor.bDeviceClass == USB_MISCELLANEOUS_DEVICE)
        {
            deviceDetails->DeviceTypeError = gcnew String("ERROR:  Multi-interface Function code " +
                    connectionInfo->DeviceDescriptor.bDeviceClass +
                    " used for device with no IAD descriptors");
        }

        if (connectionInfo->DeviceDescriptor.bDeviceClass == USB_COMMUNICATION_DEVICE  ||
                connectionInfo->DeviceDescriptor.bDeviceClass == USB_HUB_DEVICE ||
                connectionInfo->DeviceDescriptor.bDeviceClass == USB_DIAGNOSTIC_DEVICE ||
                connectionInfo->DeviceDescriptor.bDeviceClass == USB_WIRELESS_CONTROLLER_DEVICE ||
                connectionInfo->DeviceDescriptor.bDeviceClass == USB_MISCELLANEOUS_DEVICE ||
                connectionInfo->DeviceDescriptor.bDeviceClass == USB_VENDOR_SPECIFIC_DEVICE)
        {
            tog = false;
        }

        // Not an IAD device, so all subclass values are invalid
        if (connectionInfo->DeviceDescriptor.bDeviceSubClass > 0x00 &&
            connectionInfo->DeviceDescriptor.bDeviceSubClass < 0xFF)
        {
            deviceDetails->SubclassTypeError = gcnew String("ERROR:  bDeviceSubClass is invalid - " +
                                                            connectionInfo->DeviceDescriptor.bDeviceSubClass);
        }

          // Not an IAD device, so all subclass values are invalid, check protocol
        if (connectionInfo->DeviceDescriptor.bDeviceProtocol > 0x00 &&
            connectionInfo->DeviceDescriptor.bDeviceProtocol < 0xFF && tog==1)
        {
            deviceDetails->DeviceProtocolError = gcnew String("ERROR:  bDeviceProtocol is invalid - " +
                                                            connectionInfo->DeviceDescriptor.bDeviceProtocol);
        }
    }

    return tog;
}

/*****************************************************************************

  XmlAddConnectionInfo()

  This routine adds connection information for the device
 *****************************************************************************/
void XmlAddConnectionInfo(
        NodeConnectionInfoExType ^xmlConnectionInfo,
        PUSB_NODE_CONNECTION_INFORMATION_EX connectionInfo,
        PUSBDEVICEINFO deviceInfo,
        PSTRING_DESCRIPTOR_NODE stringDesc,
        PDEVICE_INFO_NODE pNode)
{
    NodeConnectionInfoExType ^ nc = xmlConnectionInfo;
    bool tog = true;
    nc->ConnectionInfoStruct = gcnew  NodeConnectionInfoExStructType();

    // Update the structure
    XmlAddConnectionInfoSt(nc->ConnectionInfoStruct, connectionInfo, pNode);

    // Add verbose fields
    if (connectionInfo->ConnectionStatus == NoDeviceConnected)
    {
        // No device connected, nothing to do
        return;
    }

    if (connectionInfo->DeviceDescriptor.iProduct)
    {
        // Add EN version of string descriptor

        nc->IProductStringDescEn = XmlGetStringDescriptor(
                connectionInfo->DeviceDescriptor.iProduct,
                stringDesc,
                true);
    }

    // Check open pipes count
    if (connectionInfo->NumberOfOpenPipes == 0)
    {
        nc->PipeInfoError = gcnew String("ERROR: No open pipes");
    }

    // Check device descriptor length
    if (connectionInfo->DeviceDescriptor.bLength != DEVICE_DESCRIPTOR_LENGTH)
    {
        nc->LengthError = gcnew String("ERROR: bLength " +
                connectionInfo->DeviceDescriptor.bLength +
                " incorrect, should be " +
                DEVICE_DESCRIPTOR_LENGTH
                );
    }

    // Check for device error
    if ((connectionInfo->ConnectionStatus == DeviceFailedEnumeration) ||
            (connectionInfo->ConnectionStatus == DeviceGeneralFailure))
    {
        nc->DeviceError = gcnew String("ERROR:  Device enumeration failure");
    }
    else
    {
        nc->DeviceClassDetails = gcnew UsbDeviceClassDetailsType();

        // Add device class details
        tog = XmlAddDeviceClassDetails(
                nc->DeviceClassDetails,
                connectionInfo,
                deviceInfo);

        nc->MaxPacketSizeInBytes = connectionInfo->DeviceDescriptor.bMaxPacketSize0;

        // Validate speed
        switch (connectionInfo->Speed)
        {
            case UsbLowSpeed:
                if (connectionInfo->DeviceDescriptor.bMaxPacketSize0 != 8)
                {
                    nc->PacketSizeError = gcnew String("ERROR:  Low Speed Devices require bMaxPacketSize0 = 8");
                }
                break;
            case UsbFullSpeed:
                if (!(connectionInfo->DeviceDescriptor.bMaxPacketSize0 == 8 ||
                            connectionInfo->DeviceDescriptor.bMaxPacketSize0 == 16 ||
                            connectionInfo->DeviceDescriptor.bMaxPacketSize0 == 32 ||
                            connectionInfo->DeviceDescriptor.bMaxPacketSize0 == 64))
                {
                    nc->PacketSizeError = gcnew String("ERROR:  Full Speed Devices require bMaxPacketSize0 = 8, 16, 32, or 64");
                }
                break;
            case UsbHighSpeed:
                if (connectionInfo->DeviceDescriptor.bMaxPacketSize0 != 64)
                {
                    nc->PacketSizeError = gcnew String("ERROR:  High Speed Devices require bMaxPacketSize0 = 64");
                }
                break;
        }

        // Get string descriptors
        nc->VendorString = PACHAR_TO_STRING(GetVendorString(connectionInfo->DeviceDescriptor.idVendor));

        nc->ManufacturerString = XmlGetStringDescriptor(connectionInfo->DeviceDescriptor.iManufacturer, stringDesc, false);
        nc->ProductString = XmlGetStringDescriptor(connectionInfo->DeviceDescriptor.iProduct, stringDesc, false);
        nc->LangIdString =  XmlGetLangIdString(connectionInfo->DeviceDescriptor.iProduct, stringDesc);
        nc->SerialString = XmlGetStringDescriptor(connectionInfo->DeviceDescriptor.iSerialNumber, stringDesc, false);

        // Validate configuration
        if (connectionInfo->DeviceDescriptor.bNumConfigurations != 1)
        {
            nc->ConfigurationCountError = gcnew String("WARNING: Most host controllers will only work with "\
                                                        "one configuration per speed");
        }
    }
    return;
}


/*****************************************************************************

  XmlAddExternalHub()

  Add a external to the parent Host Controller or hub. This is determined by the
  last object pushed on the stack
 *****************************************************************************/
HRESULT XmlAddExternalHub(PSTR ehName, PUSBEXTERNALHUBINFO ehInfo)
{
    HRESULT hr = S_OK;
    Object ^ parent = gXmlStack->Peek();
    ExternalHubType ^exHub = nullptr;

    UNREFERENCED_PARAMETER(ehName);

    if (NULL == ehInfo)
    {
        return E_FAIL;
    }

    exHub = AddExternalHub(parent);

    if (exHub != nullptr)
    {
        exHub->HubName = PACHAR_TO_STRING(ehInfo->HubName);

        if (NULL != ehInfo->UsbDeviceProperties)
        {
            exHub->HwId = PACHAR_TO_STRING(ehInfo->UsbDeviceProperties->HwId);
            exHub->DeviceId = PACHAR_TO_STRING(ehInfo->UsbDeviceProperties->DeviceId);
            exHub->ServiceName = PACHAR_TO_STRING(ehInfo->UsbDeviceProperties->Service);
            exHub->DeviceName = PACHAR_TO_STRING(ehInfo->UsbDeviceProperties->DeviceDesc);
            exHub->DeviceClass = PACHAR_TO_STRING(ehInfo->UsbDeviceProperties->DeviceClass);
        }

        exHub->HubNodeInformation = gcnew HubNodeInformationType();
        XmlAddHubNodeInformation(exHub->HubNodeInformation, ehInfo->HubInfo);

        exHub->HubInformationEx = gcnew HubInformationExType();
        XmlAddHubInformationEx(exHub->HubInformationEx, ehInfo->HubInfoEx);

        exHub->HubCapabilityEx = gcnew HubCapabilitiesExType();
        XmlAddHubCapabilitiesEx(exHub->HubCapabilityEx, ehInfo->HubCapabilityEx);

        exHub->ConnectionInfo = gcnew NodeConnectionInfoExType();

        // Update protocol
        if (NULL != ehInfo->ConnectionInfo)
        {
            switch(ehInfo->ConnectionInfo->Speed)
            {
                case UsbLowSpeed:
                case UsbFullSpeed:
                    exHub->UsbProtocol = gcnew String(USB_1_1);
                    break;
                case UsbHighSpeed:
                    exHub->UsbProtocol = gcnew String(USB_2_0);
                    break;
                case UsbSuperSpeed:
                    exHub->UsbProtocol = gcnew String(USB_3_0);
                    break;
            }
        }

        // Add connection info
        XmlAddConnectionInfo(
                exHub->ConnectionInfo,
                ehInfo->ConnectionInfo,
                (PUSBDEVICEINFO) ehInfo,
                ehInfo->StringDescs,
                ehInfo->DeviceInfoNode
                );

        // Add port connectors
        if (NULL != ehInfo->PortConnectorProps)
        {
            exHub->PortConnector = gcnew PortConnectorType();

            XmlAddPortConnectorProps(
                    exHub->PortConnector,
                    ehInfo->PortConnectorProps
                    );
        }
        // Add connection info V2
        exHub->ConnectionInfoV2 = gcnew NodeConnectionInfoExV2Type();

        XmlAddConnectionInfoV2(
                exHub->ConnectionInfoV2,
                ehInfo->ConnectionInfoV2
                );

        // Add configuration descriptor
        if (NULL != ehInfo->ConfigDesc)
        {
            exHub->DeviceConfiguration = XmlGetConfigDescriptors(
                (PUSBDEVICEINFO) ehInfo,
                (PUSB_CONFIGURATION_DESCRIPTOR) (ehInfo->ConfigDesc + 1),
                ehInfo->StringDescs
                );
        }

        // Add BOS descriptor
        if (NULL != ehInfo->BosDesc)
        {
            exHub->BosDescriptor = XmlGetBosDescriptor((PUSB_BOS_DESCRIPTOR) (ehInfo->BosDesc + 1), ehInfo->StringDescs);
        }

        gXmlStack->Push(exHub);
    }
    else
    {
        hr = E_FAIL;
    }
    return hr;
}

/*****************************************************************************

  XmlGetBosDescriptor()

  Gets the Bos descriptor object for given BOS descriptor
 *****************************************************************************/
UsbBosDescriptorType ^ XmlGetBosDescriptor(
        PUSB_BOS_DESCRIPTOR bosDesc,
        PSTRING_DESCRIPTOR_NODE stringDesc
        )
{
    PUSB_COMMON_DESCRIPTOR commonDesc = NULL;
    PUSB_DEVICE_CAPABILITY_DESCRIPTOR capDesc = NULL;
    UsbBosDescriptorType ^ bosXmlDesc = nullptr;
    ArrayList ^usb20CapExtDescList = gcnew ArrayList();
    ArrayList ^usbSuperSpeedExtDescList = gcnew ArrayList();
    ArrayList ^usbContIdCapExtDescList = gcnew ArrayList();
    ArrayList ^usbUnknownDescList = gcnew ArrayList();
    ArrayList ^usbBillboardDescList = gcnew ArrayList();

    if(NULL == bosDesc)
    {
        return nullptr;
    }

    // Initialize attributes
    bosXmlDesc = gcnew UsbBosDescriptorType();
    bosXmlDesc->BLength = bosDesc->bLength;
    bosXmlDesc->BDescriptorType = bosDesc->bDescriptorType;
    bosXmlDesc->WTotalLength = bosDesc->wTotalLength;
    bosXmlDesc->BNumDeviceCaps = bosDesc->bNumDeviceCaps;

    commonDesc = (PUSB_COMMON_DESCRIPTOR) bosDesc;

    while ((commonDesc = GetNextDescriptor((PUSB_COMMON_DESCRIPTOR) bosDesc,
                    bosDesc->wTotalLength,
                    commonDesc,
                    -1)) != NULL)
    {
        switch (commonDesc->bDescriptorType)
        {
            case USB_DEVICE_CAPABILITY_DESCRIPTOR_TYPE:
                capDesc = (PUSB_DEVICE_CAPABILITY_DESCRIPTOR)commonDesc;
                switch (capDesc->bDevCapabilityType)
                {
                    case USB_DEVICE_CAPABILITY_USB20_EXTENSION:
                        usb20CapExtDescList->Add(
                                XmlGetUsb20CapabilityExtensionDescriptor(
                                    (PUSB_DEVICE_CAPABILITY_USB20_EXTENSION_DESCRIPTOR)capDesc
                                    )
                                );
                        break;
                    case USB_DEVICE_CAPABILITY_SUPERSPEED_USB:
                        usbSuperSpeedExtDescList->Add(
                                XmlGetSuperSpeedCapabilityExtensionDescriptor(
                                    (PUSB_DEVICE_CAPABILITY_SUPERSPEED_USB_DESCRIPTOR)capDesc
                                    )
                                );
                        break;
                    case USB_DEVICE_CAPABILITY_CONTAINER_ID:
                        usbContIdCapExtDescList->Add(
                                XmlGetContainerIdCapabilityExtensionDescriptor(
                                    (PUSB_DEVICE_CAPABILITY_CONTAINER_ID_DESCRIPTOR)capDesc
                                    )
                                );
                        break;
                    case USB_DEVICE_CAPABILITY_BILLBOARD:
                        usbBillboardDescList->Add(
                            XmlGetBillboardCapabilityDescriptor(
                                (PUSB_DEVICE_CAPABILITY_BILLBOARD_DESCRIPTOR) capDesc,
                                stringDesc
                                )
                            );
                        break;
                    default:
                        usbUnknownDescList->Add(
                                XmlGetUnknownDescriptor(
                                    (PUSB_COMMON_DESCRIPTOR) capDesc
                                    )
                                );
                        break;
                }
                break;
            default:
                usbUnknownDescList->Add(XmlGetUnknownDescriptor(commonDesc));
                break;
        }
    }

    // Convert lists to arrays for and add to Bos Descriptor
    bosXmlDesc->UnknownDescriptor = reinterpret_cast<array<UsbDeviceUnknownDescriptorType ^>^> (
            usbUnknownDescList->ToArray(UsbDeviceUnknownDescriptorType::typeid)
            );
    bosXmlDesc->UsbSuperSpeedExtensionDescriptor = reinterpret_cast<array<UsbSuperSpeedExtensionDescriptorType ^>^> (
            usbSuperSpeedExtDescList->ToArray(UsbSuperSpeedExtensionDescriptorType::typeid)
            );
    bosXmlDesc->UsbUsb20ExtensionDescriptor = reinterpret_cast<array<UsbUsb20ExtensionDescriptorType ^>^> (
            usb20CapExtDescList->ToArray(UsbUsb20ExtensionDescriptorType::typeid)
            );
    bosXmlDesc->UsbDispContIdCapExtDescriptor = reinterpret_cast<array<UsbDispContIdCapExtDescriptorType ^>^> (
            usbContIdCapExtDescList->ToArray(UsbDispContIdCapExtDescriptorType::typeid)
            );
    bosXmlDesc->UsbBillboardCapabilityDescriptor = reinterpret_cast<array<UsbBillboardCapabilityDescriptorType ^>^> (
        usbBillboardDescList->ToArray(UsbBillboardCapabilityDescriptorType::typeid)
        );

    return bosXmlDesc;
}


/*****************************************************************************

  XmlGetUsb20CapabilityExtensionDescriptor()

  Gets a Usb20Capability extension descriptor object from the given descriptor
 *****************************************************************************/
UsbUsb20ExtensionDescriptorType ^ XmlGetUsb20CapabilityExtensionDescriptor(
        PUSB_DEVICE_CAPABILITY_USB20_EXTENSION_DESCRIPTOR capDesc
        )
{
    UsbUsb20ExtensionDescriptorType ^ capXmlDesc = nullptr;

    if(NULL == capDesc)
    {
        return nullptr;
    }

    capXmlDesc = gcnew UsbUsb20ExtensionDescriptorType();

    capXmlDesc->BLength = capDesc->bLength;
    capXmlDesc->BDescriptorType = capDesc->bDescriptorType;
    capXmlDesc->BDevCapabilityType = capDesc->bDevCapabilityType;
    capXmlDesc->BmAttributes = capDesc->bmAttributes.AsUlong;

    if (capDesc->bmAttributes.AsUlong & USB_DEVICE_CAPABILITY_USB20_EXTENSION_BMATTRIBUTES_RESERVED_MASK)
    {
        capXmlDesc->ReservedBitError = gcnew String("ERROR: bits 31..2 and bit 0 are reserved and must be 0");
    }
    if (capDesc->bmAttributes.LPMCapable == 1)
    {
        capXmlDesc->SupportsLinkPowerManagement = true;
    }

    return capXmlDesc;
}

/*****************************************************************************

  XmlGetSuperSpeedCapabilityExtensionDescriptor()

  Gets a Super speed capability extension descriptor object from the given descriptor
 *****************************************************************************/
UsbSuperSpeedExtensionDescriptorType ^ XmlGetSuperSpeedCapabilityExtensionDescriptor(
        PUSB_DEVICE_CAPABILITY_SUPERSPEED_USB_DESCRIPTOR capDesc
        )
{
    UsbSuperSpeedExtensionDescriptorType ^ capXmlDesc = nullptr;

    if(NULL == capDesc)
    {
        return nullptr;
    }

    capXmlDesc = gcnew UsbSuperSpeedExtensionDescriptorType();

    capXmlDesc->BLength = capDesc->bLength;
    capXmlDesc->BDescriptorType = capDesc->bDescriptorType;
    capXmlDesc->BDevCapabilityType = capDesc->bDevCapabilityType;
    capXmlDesc->BmAttributes = capDesc->bmAttributes;
    capXmlDesc->BU1DevExitLat = capDesc->bU1DevExitLat;
    capXmlDesc->WSpeedsSupported = capDesc->wSpeedsSupported;
    capXmlDesc->WU2DevExitLat = capDesc->wU2DevExitLat;
    capXmlDesc->BFunctionalitySupport = capDesc->bFunctionalitySupport;

    // Add descriptive fields
    if (capDesc->bmAttributes & USB_DEVICE_CAPABILITY_SUPERSPEED_BMATTRIBUTES_RESERVED_MASK)
    {
        capXmlDesc->ReservedAttributesBitError = gcnew String("ERROR: bits 7:2 and bit 0 are reserved");
    }
    if (capDesc->bmAttributes & USB_DEVICE_CAPABILITY_SUPERSPEED_BMATTRIBUTES_LTM_CAPABLE)
    {
        capXmlDesc->LatencyToleranceMsgCapable = true;
    }
    if (capDesc->wSpeedsSupported & USB_DEVICE_CAPABILITY_SUPERSPEED_SPEEDS_SUPPORTED_LOW)
    {
        capXmlDesc->SupportsLowSpeed = true;
    }
    if (capDesc->wSpeedsSupported & USB_DEVICE_CAPABILITY_SUPERSPEED_SPEEDS_SUPPORTED_FULL)
    {
        capXmlDesc->SupportsFullSpeed = true;
    }
    if (capDesc->wSpeedsSupported & USB_DEVICE_CAPABILITY_SUPERSPEED_SPEEDS_SUPPORTED_HIGH)
    {
        capXmlDesc->SupportsHighSpeed = true;
    }
    if (capDesc->wSpeedsSupported & USB_DEVICE_CAPABILITY_SUPERSPEED_SPEEDS_SUPPORTED_SUPER)
    {
        capXmlDesc->SupportsSuperSpeed = true;
    }
    if (capDesc->wSpeedsSupported & USB_DEVICE_CAPABILITY_SUPERSPEED_SPEEDS_SUPPORTED_RESERVED_MASK)
    {
        capXmlDesc->ReservedSpeedError = gcnew String("ERROR: bits 15:4 are reserved");
    }


    switch (capDesc->bFunctionalitySupport)
    {
        case UsbLowSpeed:
            capXmlDesc->LowestSpeed  = gcnew String("low-speed");
            break;
        case UsbFullSpeed:
            capXmlDesc->LowestSpeed  = gcnew String("full-speed");
            break;
        case UsbHighSpeed:
            capXmlDesc->LowestSpeed  = gcnew String("high-speed");
            break;
        case UsbSuperSpeed:
            capXmlDesc->LowestSpeed  = gcnew String("SuperSpeed");
            break;
        default:
            capXmlDesc->LowestSpeed  = gcnew String("ERROR: Invalid value");
            break;
    }

    if (capDesc->bU1DevExitLat <= USB_DEVICE_CAPABILITY_SUPERSPEED_U1_DEVICE_EXIT_MAX_VALUE)
    {
        capXmlDesc->U1DevExitLatencyString  = String::Format("Less than {0} micro-seconds", capDesc->bU1DevExitLat);
    }
    else
    {
        capXmlDesc->U1DevExitLatencyString  = gcnew String("ERROR: Invalid value");
    }

    if (capDesc->wU2DevExitLat <= USB_DEVICE_CAPABILITY_SUPERSPEED_U2_DEVICE_EXIT_MAX_VALUE)
    {
        capXmlDesc->U2DevExitLatencyString  = String::Format("Less than {0} micro-seconds", capDesc->wU2DevExitLat);
    }
    else
    {
        capXmlDesc->U2DevExitLatencyString  = gcnew String("ERROR: Invalid value");
    }

    return capXmlDesc;
}

/*****************************************************************************

  XmlGetContainerIdCapabilityExtensionDescriptor()

  Gets a Usb20Capability extension descriptor object from the given descriptor
 *****************************************************************************/
UsbDispContIdCapExtDescriptorType ^ XmlGetContainerIdCapabilityExtensionDescriptor(
        PUSB_DEVICE_CAPABILITY_CONTAINER_ID_DESCRIPTOR capDesc
        )
{
    UsbDispContIdCapExtDescriptorType ^ capXmlDesc = nullptr;
    LPGUID pGuid = NULL;

    if(NULL == capDesc)
    {
        return nullptr;
    }

    capXmlDesc = gcnew UsbDispContIdCapExtDescriptorType();

    capXmlDesc->BLength = capDesc->bLength;
    capXmlDesc->BDescriptorType = capDesc->bDescriptorType;
    capXmlDesc->BDevCapabilityType = capDesc->bDevCapabilityType;
    capXmlDesc->BReserved = capDesc->bReserved;

    if (capDesc->bReserved != 0)
    {
        capXmlDesc->ReservedBitError = gcnew String("ERROR: field is reserved and should be zero");
    }

    pGuid = (LPGUID) capDesc->ContainerID;

    capXmlDesc->ContainerIdStr = String::Format("{0:X}-{1:X}-{2:X}-{3:X}{4:X}-{5:X}{6:X}{7:X}{8:X}{9:X}{10:X}",
            pGuid->Data1,
            pGuid->Data2,
            pGuid->Data3,
            pGuid->Data4[0],
            pGuid->Data4[1],
            pGuid->Data4[2],
            pGuid->Data4[3],
            pGuid->Data4[4],
            pGuid->Data4[5],
            pGuid->Data4[6],
            pGuid->Data4[7]);

    return capXmlDesc;
}


/*****************************************************************************

XmlGetBillboardCapabilityDescriptor()

Gets a billboard capability descriptor from a given descriptor
*****************************************************************************/
UsbBillboardCapabilityDescriptorType ^ XmlGetBillboardCapabilityDescriptor(
        PUSB_DEVICE_CAPABILITY_BILLBOARD_DESCRIPTOR capDesc,
        PSTRING_DESCRIPTOR_NODE stringDesc
        )
{
    UCHAR                                  i = 0;
    UCHAR                                  bNumAlternateModes = 0;
    UCHAR                                  alternateModeConfiguration = 0;
    UsbBillboardCapabilityDescriptorType ^ capXmlDesc = nullptr;
    UsbBillboardSVIDType ^                 svidXmlDesc = nullptr;
    ArrayList ^                            usbSVIDDescList = gcnew ArrayList();


    if (NULL == capDesc)
    {
        return nullptr;
    }

    capXmlDesc = gcnew UsbBillboardCapabilityDescriptorType();


    capXmlDesc->BLength = capDesc->bLength;
    capXmlDesc->BDescriptorType = capDesc->bDescriptorType;
    capXmlDesc->BDevCapabilityType = capDesc->bDevCapabilityType;
    capXmlDesc->IAddtionalInfoURL = capDesc->iAddtionalInfoURL;
    capXmlDesc->BNumberOfAlternateModes = capDesc->bNumberOfAlternateModes;
    capXmlDesc->BPreferredAlternateMode = capDesc->bPreferredAlternateMode;
    capXmlDesc->CalculatedBLength = sizeof(USB_DEVICE_CAPABILITY_BILLBOARD_DESCRIPTOR) +
            sizeof(capDesc->AlternateMode[0]) * (capDesc->bNumberOfAlternateModes - 1);
    capXmlDesc->BillboardDescriptorErrors = gcnew String("");
    capXmlDesc->AddtionalInfoURL = XmlGetStringDescriptor(
            capDesc->iAddtionalInfoURL,
            stringDesc,
            false
            );

    if (capDesc->VconnPower.NoVconnPowerRequired)
    {
        capXmlDesc->VConnPower = gcnew String("The adapter does not require Vconn Power. Bits 2..0 ignored");
    }
    else
    {
        switch (capDesc->VconnPower.VConnPowerNeededForFullFunctionality)
        {
        case 0:
            capXmlDesc->VConnPower = gcnew String("1W needed by adapter for full functionality");
            break;
        case 1:
            capXmlDesc->VConnPower = gcnew String("1.5W needed by adapter for full functionality");
            break;
        case 7:
            capXmlDesc->BillboardDescriptorErrors += "ERROR: VConnPowerNeededForFullFunctionality - Reserved value being used";
            break;
        default:
            capXmlDesc->VConnPower = gcnew String(String::Format("{0} W needed by adapter for full functionality", capDesc->VconnPower.VConnPowerNeededForFullFunctionality));
        }
    }

    if (capDesc->bNumberOfAlternateModes > BILLBOARD_MAX_NUM_ALT_MODE)
    {
        capXmlDesc->BillboardDescriptorErrors += "ERROR: Invalid bNumberofAlternateModes; ";
    }
    if (capDesc->VconnPower.Reserved)
    {
        capXmlDesc->BillboardDescriptorErrors += "ERROR: Reserved bits in VCONN Power being used; ";
    }
    if (capDesc->bReserved)
    {
        capXmlDesc->BillboardDescriptorErrors += "ERROR: bReserved being used; ";
    }

    bNumAlternateModes = capDesc->bNumberOfAlternateModes;
    if (bNumAlternateModes > BILLBOARD_MAX_NUM_ALT_MODE)
    {
        bNumAlternateModes = BILLBOARD_MAX_NUM_ALT_MODE;
    }
    for (i = 0; i < bNumAlternateModes; i++)
    {
        svidXmlDesc = gcnew UsbBillboardSVIDType();
        alternateModeConfiguration = ((capDesc->bmConfigured[i / 4]) >> ((i % 4) * 2)) & 0x3;
        svidXmlDesc->WSVID = capDesc->AlternateMode[i].wSVID;
        svidXmlDesc->BAlternateMode = capDesc->AlternateMode[i].bAlternateMode;
        svidXmlDesc->IAlternateModeString = capDesc->AlternateMode[i].iAlternateModeSetting;
        svidXmlDesc->AlternateModeString = XmlGetStringDescriptor(
            capDesc->AlternateMode[i].iAlternateModeSetting,
            stringDesc,
            false
            );

        switch (alternateModeConfiguration)
        {
        case 0:
            svidXmlDesc->Description = gcnew String("Unspecified Error");
            break;
        case 1:
            svidXmlDesc->Description = gcnew String("Alternate Mode configuration not attempted");
            break;
        case 2:
            svidXmlDesc->Description = gcnew String("Alternate Mode configuration attempted but unsuccessful");
            break;
        case 3:
            svidXmlDesc->Description = gcnew String("Alternate Mode configuration successful");
            break;
        }
        usbSVIDDescList->Add(svidXmlDesc);
    }
    capXmlDesc->UsbBillboardSVID = reinterpret_cast<array<UsbBillboardSVIDType ^>^> (
        usbSVIDDescList->ToArray(UsbBillboardSVIDType::typeid));

    return capXmlDesc;
}

/*****************************************************************************

  XmlAddUsbDevice()

  Add a external to the parent Host Controller or hub. This is determined by the
  last object pushed on the stack
 *****************************************************************************/
HRESULT XmlAddUsbDevice(PSTR devName, PUSBDEVICEINFO deviceInfo)
{
    HRESULT hr = S_OK;
    Object ^ parent = gXmlStack->Peek();
    UsbDeviceType ^usbDevice = nullptr;
    NoDeviceType ^noDevice = nullptr;

    if (NULL == deviceInfo)
    {
        return E_FAIL;
    }

    if (deviceInfo->ConfigDesc == NULL)
    {
        // There is no USB device on this port, add a NoDevice type here instead of USB device
        noDevice = AddDisconnectedPort(parent);

        if (nullptr != noDevice)
        {
            noDevice->UsbPortNumber = gcnew String("");
            noDevice->UsbPortNumber += deviceInfo->ConnectionInfo->ConnectionIndex;
            noDevice->Name = PACHAR_TO_STRING(devName);
        }
        else
        {
            hr = E_FAIL;
        }
    }

    else
    {
        usbDevice = AddUsbDevice(parent);

        if (nullptr != usbDevice)
        {
            // Update device information
            if (NULL != deviceInfo->UsbDeviceProperties)
            {
                usbDevice->HwId = PACHAR_TO_STRING(deviceInfo->UsbDeviceProperties->HwId);
                usbDevice->DeviceId = PACHAR_TO_STRING(deviceInfo->UsbDeviceProperties->DeviceId);
                usbDevice->ServiceName = PACHAR_TO_STRING(deviceInfo->UsbDeviceProperties->Service);
                usbDevice->DeviceName = PACHAR_TO_STRING(deviceInfo->UsbDeviceProperties->DeviceDesc);
                usbDevice->DeviceClass = PACHAR_TO_STRING(deviceInfo->UsbDeviceProperties->DeviceClass);
            }

            // Update port number
            usbDevice->UsbPortNumber = gcnew String("");
            usbDevice->UsbPortNumber += deviceInfo->ConnectionInfo->ConnectionIndex;
            usbDevice->ConnectionInfo = gcnew NodeConnectionInfoExType();

            // Update protocol
            if (NULL != deviceInfo->ConnectionInfo)
            {
                switch(deviceInfo->ConnectionInfo->Speed)
                {
                    case UsbLowSpeed:
                    case UsbFullSpeed:
                        usbDevice->UsbProtocol = gcnew String(USB_1_1);
                        break;
                    case UsbHighSpeed:
                        usbDevice->UsbProtocol = gcnew String(USB_2_0);
                        break;
                    case UsbSuperSpeed:
                        usbDevice->UsbProtocol = gcnew String(USB_3_0);
                        break;
                }
            }

            // Add connection info
            XmlAddConnectionInfo(
                    usbDevice->ConnectionInfo,
                    deviceInfo->ConnectionInfo,
                    (PUSBDEVICEINFO) deviceInfo,
                    deviceInfo->StringDescs,
                    deviceInfo->DeviceInfoNode
                    );

            // Add port connector
            if (NULL != deviceInfo->PortConnectorProps)
            {
                usbDevice->PortConnector =  gcnew PortConnectorType();

                XmlAddPortConnectorProps(
                        usbDevice->PortConnector,
                        deviceInfo->PortConnectorProps
                        );
            }

            // Add connectiontion info V2
            if (NULL != deviceInfo->ConnectionInfoV2)
            {
                usbDevice->ConnectionInfoV2 = gcnew NodeConnectionInfoExV2Type();
                XmlAddConnectionInfoV2(
                        usbDevice->ConnectionInfoV2,
                        deviceInfo->ConnectionInfoV2
                        );
            }

            // Add configuration descriptor
            if (NULL != deviceInfo->ConfigDesc)
            {
                // The device configuration is allocated by XmlGetConfigDescriptors()
                usbDevice->DeviceConfiguration = XmlGetConfigDescriptors(
                        (PUSBDEVICEINFO) deviceInfo,
                        (PUSB_CONFIGURATION_DESCRIPTOR) (deviceInfo->ConfigDesc + 1),
                        deviceInfo->StringDescs
                        );
            }

            // Add BOS descriptor
            if (NULL != deviceInfo->BosDesc)
            {
                usbDevice->BosDescriptor = XmlGetBosDescriptor(
                        (PUSB_BOS_DESCRIPTOR) (deviceInfo->BosDesc + 1),
                        deviceInfo->StringDescs
                        );
            }
        }
        else
        {
            hr = E_FAIL;
        }
    }
    return hr;
}

/*****************************************************************************

  XmlAddRootHub()

  Add a root hub to the parent Host Controller
 *****************************************************************************/
HRESULT XmlAddRootHub(PSTR rhName, PUSBROOTHUBINFO rhInfo)
{
    HRESULT hr = S_OK;
    Object ^ parent = gXmlStack->Peek();
    HostControllerType ^ hcParent = nullptr;
    PSTR rootHubName = rhInfo->HubName;

    UNREFERENCED_PARAMETER(rhName);

    hcParent = dynamic_cast<HostControllerType ^> (parent);

    if (hcParent != nullptr)
    {
        RootHubType ^ rh = nullptr;
        hcParent = (HostControllerType ^) parent;
        hcParent->RootHub = gcnew RootHubType();

        rh = hcParent->RootHub;
        rh->HubName = PACHAR_TO_STRING(rootHubName);

        if (NULL != rhInfo->UsbDeviceProperties)
        {
            rh->HwId = PACHAR_TO_STRING(rhInfo->UsbDeviceProperties->HwId);
            rh->DeviceId = PACHAR_TO_STRING(rhInfo->UsbDeviceProperties->DeviceId);
            rh->ServiceName = PACHAR_TO_STRING(rhInfo->UsbDeviceProperties->Service);
            rh->DeviceName = PACHAR_TO_STRING(rhInfo->UsbDeviceProperties->DeviceDesc);
            rh->DeviceClass = PACHAR_TO_STRING(rhInfo->UsbDeviceProperties->DeviceClass);

            // Roothub protocol is same as HC protocol
            rh->UsbProtocol = hcParent->UsbProtocol;
        }

        rh->HubNodeInformation = gcnew HubNodeInformationType();
        XmlAddHubNodeInformation(rh->HubNodeInformation, rhInfo->HubInfo);

        rh->HubInformationEx = gcnew HubInformationExType();
        XmlAddHubInformationEx(rh->HubInformationEx, rhInfo->HubInfoEx);

        rh->HubCapabilityEx = gcnew HubCapabilitiesExType();
        XmlAddHubCapabilitiesEx(rh->HubCapabilityEx, rhInfo->HubCapabilityEx);

        // Push root hub on to stack
        gXmlStack->Push(rh);
    }
    else
    {
        // Root hub should be connected to a host controller
        hr = E_FAIL;
    }
    return S_OK;
}

/*****************************************************************************

  XmlSetVersion()

  Set version information in XML tree
 *****************************************************************************/
VOID XmlSetVersion(
        UCHAR uvcMajorVersion,
        UCHAR uvcMinorVersion,
        UCHAR uvcMajorSpecVersion,
        UCHAR uvcMinorSpecVersion
        )
{
    MachineInfoType ^ mInfo;
    gXmlView->MachineInfo = gcnew MachineInfoType();

    mInfo = gXmlView->MachineInfo;

    mInfo->UvcMajorVersion = uvcMajorVersion;
    mInfo->UvcMinorVersion = uvcMinorVersion;
    mInfo->UvcMajorSpecVersion = uvcMajorSpecVersion;
    mInfo->UvcMinorSpecVersion = uvcMinorSpecVersion;

}

/*****************************************************************************

  InitXmlHelper()

  Initialize XML helper
 *****************************************************************************/
HRESULT InitXmlHelper()
{
    HRESULT hr = S_OK;

    (XmlGlobal::Instance())->ViewAll = gcnew UvcViewAll();
    (XmlGlobal::Instance())->ViewAll->UvcView = gcnew UvcViewType();

    //
    // Initialize fields to null so we can check against them for allocation
    //
    (XmlGlobal::Instance())->ViewAll->UvcView->MachineInfo = nullptr;
    (XmlGlobal::Instance())->ViewAll->UvcView->UsbTree = nullptr;

    XmlSetVersion(
            UVC_SPEC_MAJOR_VERSION,
            UVC_SPEC_MINOR_VERSION,
            USBVIEW_MAJOR_VERSION,
            USBVIEW_MINOR_VERSION
            );

    gXmlStack->Push(gXmlView);

    gXmlViewInitialized = TRUE;

    return hr;
}

/*****************************************************************************

  SaveXml()

  Saves the inmemory USB view as XML file
 *****************************************************************************/
HRESULT SaveXml(LPTSTR szfileName, DWORD dwCreationDisposition)
{
    HRESULT hr = S_OK;

    if (gXmlViewInitialized)
    {
        try
        {
            String ^fileName = PACHAR_TO_STRING(szfileName);
            XmlSerializer ^ serializer = gcnew XmlSerializer(UvcViewAll::typeid);
            TextWriter ^ writer = nullptr;

            if (dwCreationDisposition != CREATE_ALWAYS)
            {
                // Check if file exits and return failure if it does
                if (File::Exists(fileName))
                {
                    hr =  HRESULT_FROM_WIN32(ERROR_FILE_EXISTS);
                }
            }

            // Check if file name is NULL
            if (String::IsNullOrEmpty(fileName))
            {
                hr = E_INVALIDARG;
            }

            if (SUCCEEDED(hr))
            {
                writer = gcnew StreamWriter(fileName);
                serializer->Serialize(writer, (XmlGlobal::Instance())->ViewAll);
                writer->Close();
            }

            // Release and reinit XML View for next iteration if requested
            ReleaseXmlWriter();
            InitXmlHelper();

        }
        catch(Exception ^ ex)
        {
            hr = (HRESULT) Marshal::GetHRForException(ex);
        }
    }
    else
    {
        hr = E_FAIL;
    }

    return hr;
}


/*****************************************************************************

  ReleaseXmlWriter()

 *****************************************************************************/
HRESULT ReleaseXmlWriter()
{
    HRESULT hr = S_OK;

    if (gXmlViewInitialized)
    {
        gXmlViewInitialized = FALSE;
        delete XmlGlobal::Instance();
    }

    return hr;
}

